BZOJ3329Xorequ——数位DP+矩阵乘法

Description
这里写图片描述
Input

第一行一个正整数,表示数据组数据 ,接下来T行
每行一个正整数N

Output

2*T行
第2*i-1行表示第i个数据中问题一的解,

第2*i行表示第i个数据中问题二的解,

Sample Input

1

1

Sample Output

1

2

HINT

x=1与x=2都是原方程的根,注意第一个问题的解

不要mod 10^9+7

1<=N<=10^18

1<=T<=1000


我们考虑从题意里获得点东西x^3x==2x → x^3x^x==2x^x → 3x^x^x==2x^x
→ 3x^0==2x^x → 3x==2x^x → x^2x==3x而我们都知道x+2x==3x,而^是二进制上不进位的加法,x^2x的效果与x+2x相同,说明 x 2 x 任意一位上都不可能同时是1。然后 2 x x 左移一位,所以意味着 x 不存在相邻的两位,满足这两位上都是1。于是我们就可以用数位DP来求取 x 的方案数。
f [ i ] [ j ] [ 0 / 1 ] 表示第i位,上面为j(0或1),并且是否被约束。最后的答案就是f[cnt][0][0]+f[cnt][0][1]+f[cnt][1][0]+f[cnt][1][1]-1(cnt表示x二进制下的位数,-1是因为要舍去0这种情况)
对于第二问我们要推一推结论,我们手算几个,设 f ( x ) 表示 2 x 时的答案,则 f ( 0 ) = 1 , f ( 1 ) = 2 , f ( 2 ) = 3 , f ( 3 ) = 5 , f ( 5 ) = 8 发现 f ( x ) 刚好满足斐波那契数列(只不过开头只有1个1),所以对于 f ( n ) 我们直接用矩阵乘法加速即可。
#include<bits/stdc++.h>
#define MD 1000000007
#define ll long long
using namespace std;
ll read(){
    char c;ll x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
void print(ll x){
    if(x/10) print(x/10);
    putchar(x%10+'0');
}
ll T,n,cnt,ans,num[61],f[61][2][2],A[2][2],B[2][2],U[2][2];
void mul(ll a[][2],ll b[][2]){
    for(ll i=0;i<2;i++)
     for(ll j=0;j<2;j++){
        ll res=0;
        for(ll k=0;k<2;k++) res=(res+a[i][k]*b[k][j])%MD;
        U[i][j]=res;
     }
    for(ll i=0;i<2;i++)
     for(ll j=0;j<2;j++)
      a[i][j]=U[i][j];
}
int main()
{
    T=read();
    while(T--){
        memset(f,0,sizeof(f));
        n=read();ll p=n;cnt=0;ans=0;
        while(p){
            cnt++;
            if(p&1) num[cnt]=1;
            else num[cnt]=0;
            p/=2;
        }
        f[0][0][0]=1;
        for(ll i=0;i<cnt;i++)
         for(ll k=0;k<2;k++){
            if(f[i][0][k]){
                f[i+1][0][k|(0<num[cnt-i])]+=f[i][0][k];
                if(k||num[cnt-i]==1)f[i+1][1][k]+=f[i][0][k];
            }
            if(f[i][1][k]){
                f[i+1][0][k|(0<num[cnt-i])]+=f[i][1][k];
            }
         }
        ans=(f[cnt][0][0]+f[cnt][0][1]+f[cnt][1][0]+f[cnt][1][1]);
        print(ans-1);puts("");
        A[0][0]=2;A[0][1]=1;A[1][0]=A[1][1]=0;B[0][0]=B[0][1]=B[1][0]=1;B[1][1]=0;
        if(n==0){puts("1");continue;}
        p=(n-1);
        while(p){
            if(p&1) mul(A,B);
            mul(B,B);p/=2;
        }
        print(A[0][0]);puts("");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/stevensonson/article/details/81063904