http://www.lydsy.com/JudgeOnline/problem.php?id=3329
题意:已知方程x xor 3x=2x,给定N,求该方程有多少个不大于N的正整数解,有多少个不大于2^N的正整数解,第二问的答案对1000000007取模。N<=10^18。
方程等价于x xor 2x=3x,又有x+2x=3x,易知x and 2x=0。
再注意到2x实际上就是x<<1,容易发现该方程成立的充要条件是x的二进制表达没有2个连续的1。
注意到2^N和0都一定是方程的解,因此第二问可以等价于,求N位的01序列,有多少种满足没有2个连续的1。
考虑递推式,若首位是1,则第二位必然是0,第三位开始则又可以任意选择,若首位是0,则第二位开始仍旧可以任意选择。
设F[N]为答案,显然有F[N]=F[N-1]+F[N-2],这就是著名的斐波那契数列。求第N项取模的结果用矩阵乘法。
考虑第一问,这是一个数位DP。不妨举例N=22,将N+1=23表达为二进制:10111。
第一段:0~1111,这相当于是第二问的一种情况,贡献即为F[4]。
第二段:10000~10011,忽略前三位,这又相当于是第二问的一种情况,贡献即为F[2]。
第三段:10100~10101,同理,贡献即为F[1]。
第四段:10110~10111,由于已经出现了连续两位1,显然后面的数一定不是解,提前结束数位DP。
总贡献即为F[4]+F[2]+F[1]=13。注意到我们这里统计的是0~N的解的数量,一定要除去0,因此最终答案为12。
两问都顺利解决。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#define size 2
#define mod 1000000007
long long T,n,i,ans,m;
long long fib[70];
int w[70];
struct M{
long long v[size][size];
M(){
memset(v,0,sizeof(v));
}
friend M operator*(M a,M b){
M ans;
for(int i=0;i<size;i++)
for(int j=0;j<size;j++)
for(int k=0;k<size;k++)
ans.v[i][j]=(ans.v[i][j]+a.v[i][k]*b.v[k][j])%mod;
return ans;
}
friend M operator^(M a,long long b){
M ans;
for(int i=0;i<size;i++)
ans.v[i][i]=1;
for(long long i=b;i;i=i/2,a=a*a)
if(i&1)ans=ans*a;
return ans;
}
}a,b;
int main(){
fib[0]=0;fib[1]=1;for(i=2;i<=64;i++) fib[i]=fib[i-1]+fib[i-2];
a.v[0][0]=a.v[0][1]=a.v[1][0]=1;a.v[1][1]=0;
scanf("%d",&T);
while(T--){
scanf("%lld",&n);++n;
b=a^n;
m=0;
while(n) w[++m]=n%2,n/=2;
w[m+1]=ans=0;
for(i=m;i;i--){
if(w[i]) ans+=fib[i+1];
if(w[i]&&w[i+1]) break;
}
printf("%lld\n%lld\n",ans-1,b.v[0][0]);
}
}