打表发现解的二进制位没有挨着的两个1
问题一数位dp解决
打表发现问题二的答案是
fib(n+2)
f
i
b
(
n
+
2
)
矩阵乘法解决
代码如下:
#include<cstring>
#include<ctype.h>
#include<cstdio>
#define MOD 1000000007
using namespace std;
typedef long long LL;
LL read(){
LL x=0,f=1;char c;
do c=getchar(),f=c=='-'?-1:f; while(!isdigit(c));
do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
return x*f;
}
struct Matrix{
LL a[3][3];
Matrix operator * (const Matrix b) const {
Matrix tmp;
tmp.a[1][1]=(a[1][1]*b.a[1][1]+a[1][2]*b.a[2][1])%MOD;
tmp.a[1][2]=(a[1][1]*b.a[1][2]+a[1][2]*b.a[2][2])%MOD;
tmp.a[2][1]=(a[2][1]*b.a[1][1]+a[2][2]*b.a[2][1])%MOD;
tmp.a[2][2]=(a[2][1]*b.a[1][2]+a[2][2]*b.a[2][2])%MOD;
return tmp;
}
}a;
int s[73],s1[73],top;
LL T,n;
LL f[73][2][2];///第i位,这一位是j,贴着边界
inline LL dp(LL x){
top=0;
while(x){
s1[++top]=x&1;
x>>=1;
}
for(int i=1;i<=top;i++) s[i]=s1[top-i+1];
memset(f,0,sizeof f);
for(int i=0;i<=1;i++) if(i<=s[i]) f[1][i][i==s[1]]++;
for(int i=2;i<=top;i++)///长度
for(int j=0;j<=1;j++)///这一位
for(int k=0;k<=1;k++){///上一位
if(k==j && k==1) continue;
for(int l=0;l<=1;l++){///是否贴着边界
if(l==1 && j>s[i]) continue;
f[i][j][(j==s[i])&l]+=f[i-1][k][l];
}
}
LL ans=0;
for(int i=0;i<=1;i++)
ans=ans+f[top][i][1]+f[top][i][0];
return ans;
}
Matrix _Pow(LL k){
Matrix sum;
sum.a[1][2]=sum.a[2][1]=0;
sum.a[1][1]=sum.a[2][2]=1;
Matrix tmp=a;
while(k){
if(k&1) sum=sum*tmp;
tmp=tmp*tmp;
k>>=1;
}
return sum;
}
LL solve(LL k){
a.a[1][1]=0;
a.a[1][2]=a.a[2][1]=a.a[2][2]=1;
Matrix ans=_Pow(k-1);
return ans.a[2][2];
}
main(){
T=read();
while(T--){
n=read();
printf("%lld\n%lld\n",dp(n)-1,solve(n+2));
}
return 0;
}