x
⊕
3
x
=
2
x
xoplus3x=2x
x⊕3x=2x 推出
x
⊕
2
x
=
3
x
xoplus2x=3x
x⊕2x=3x,然后又有
x
+
2
x
=
3
x
x+2x=3x
x+2x=3x。
定理:若
a
⊕
b
=
c
aoplus b=c
a⊕b=c 且
a
+
b
=
c
a+b=c
a+b=c,则不可能存在
a
a
a、
b
b
b 在二进制下的某一位都是
1
1
1。
证明:设
a
a
a 在二进制下的第
i
i
i 位为
a
i
a_i
ai,
b
b
b 在二进制下的第
i
i
i 位为
b
i
b_i
bi,
c
c
c 在二进制下的第
i
i
i 位为
c
i
c_i
ci。显然有
a
1
+
b
1
=
c
1
a_1+b_1=c_1
a1+b1=c1 且
a
1
⊕
b
1
=
c
1
a_1oplus b_1=c_1
a1⊕b1=c1,因为这一位不可能得到进位。分类讨论一下发现
a
1
a_1
a1 和
b
1
b_1
b1 不可能都是
1
1
1,那么第一位就不可能向第二位进位。第二位也就能用同样的方法证出来
a
2
a_2
a2 和
b
2
b_2
b2。以此类推,
a
a
a 和
b
b
b 二进制下的任何一位都不能全是
1
1
1。
那么我们就可以知道
x
x
x 和
2
x
2x
2x 二进制下的任何一位都不能全是
1
1
1。
又因为
2
x
2x
2x 是由
x
x
x 左移一位得到的,所以就等价为:二进制下的
x
x
x 任意相邻的两位都不能是
1
1
1。
那么对于第一问,把
x
x
x 转成二进制数,然后数位dp。
对于第二问:
设
f
i
,
0
/
1
f_{i,0/1}
fi,0/1 代表满足以下条件的二进制数(可以有前导0)的个数:
i
i
i 位数,第一位为
0
/
1
0/1
0/1 ,没有连续的两位为
1
1
1。
那么显然第二问的答案就是
f
n
=
f
n
,
0
+
f
n
,
1
f_n=f_{n,0}+f_{n,1}
fn=fn,0+fn,1。
考虑
f
i
f_i
fi 的状态转移:
f
i
,
0
=
f
i
−
1
,
0
+
f
i
−
1
,
1
=
f
i
−
1
f_{i,0}=f_{i-1,0}+f_{i-1,1}=f_{i-1}
fi,0=fi−1,0+fi−1,1=fi−1
f
i
,
1
=
f
i
−
1
,
0
f_{i,1}=f_{i-1,0}
fi,1=fi−1,0
得
f
i
=
f
i
−
1
+
f
i
−
1
,
0
=
f
i
−
1
+
f
i
−
2
f_i=f_{i-1}+f_{i-1,0}=f_{i-1}+f_{i-2}
fi=fi−1+fi−1,0=fi−1+fi−2
那么这就是一个斐波那契数列,用矩阵快速幂求一下就好了。
代码如下:
#include<bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;
struct Matrix
{
ll a[2][2];
Matrix(){memset(a,0,sizeof(a));}
void init(){a[0][0]=a[1][1]=1;}
Matrix operator * (const Matrix &b)
{
Matrix c;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
c.a[i][j]=(c.a[i][j]+a[i][k]*b.a[k][j])%mod;
return c;
}
}ch;
int t;
int cnt,num[100];
ll n,dp[100][2][2];
void init()
{
ch.a[1][0]=ch.a[0][1]=ch.a[1][1]=1;
}
ll dfs(int k,bool limit,bool last)
{
if(!k) return 1;
if(!dp[k][limit][last])
{
dp[k][limit][last]+=dfs(k-1,limit&&!num[k],0);
if((!last)&&((!limit)||(limit&&num[k]))) dp[k][limit][last]+=dfs(k-1,limit&&num[k],1);
}
return dp[k][limit][last];
}
ll solve1()
{
cnt=0;
ll x=n;
while(x)
{
num[++cnt]=x&1ll;
x>>=1ll;
}
return dfs(cnt,1,0)-1;
}
ll solve2()
{
Matrix a=ch,ans;
ans.init();
ll b=n+2;
while(b)
{
if(b&1ll) ans=ans*a;
a=a*a;
b>>=1ll;
}
return ans.a[0][1];
}
int main()
{
init();
scanf("%d",&t);
while(t--)
{
memset(dp,0,sizeof(dp));
scanf("%lld",&n);
printf("%lld
",solve1());
printf("%lld
",solve2());
}
return 0;
}