BZOJ3329 : Xorequ

第一问:

打表可得规律:当且仅当x&(x<<1)=0时才会是解,于是数位DP

f[i][j][k]表示二进制中前i位,上一位是j,前i位是否等于n的方案数

第二问:

打表可得规律:答案为斐波那契数列第n+2项,矩阵快速幂即可

 

#include<cstdio>

typedef long long ll;

struct mat{

  ll a[2][2];

  mat(){a[0][0]=a[0][1]=a[1][0]=a[1][1]=0;}

  mat operator*(mat b){

    mat c;

    for(int i=0,j,k;i<2;i++)for(j=0;j<2;j++)for(k=0;k<2;k++)(c.a[i][j]+=a[i][k]*b.a[k][j])%=1000000007;

    return c;

  }

}A,B,C;

int T,a[65],len,i,j,t;ll n,x,tmp,f[65][2][2];

int main(){

  scanf("%d",&T);

  while(T--){

    scanf("%lld",&x);

    for(len=0,tmp=x;tmp;a[++len]=tmp&1LL,tmp>>=1LL);

    for(i=1,j=len;i<=len&&i<j;i++,j--)t=a[i],a[i]=a[j],a[j]=t;

    for(i=1;i<=len;i++)f[i][0][0]=f[i][1][0]=f[i][0][1]=f[i][1][1]=0;

    for(i=0;i<=1;i++)f[1][i][i==a[1]]=1;

    for(i=1;i<len;i++){

      if(f[i][0][0])for(j=0;j<=1;j++)f[i+1][j][0]+=f[i][0][0];

      if(f[i][1][0])for(j=0;j<=0;j++)f[i+1][j][0]+=f[i][1][0];

      if(f[i][0][1])for(j=0;j<=a[i+1];j++)f[i+1][j][j==a[i+1]]+=f[i][0][1];

      if(f[i][1][1])for(j=0;j<=0;j++)f[i+1][j][j==a[i+1]]+=f[i][1][1];

    }

    printf("%lld\n",f[len][0][0]+f[len][1][0]+f[len][0][1]+f[len][1][1]-1);

    for(A=B=C=mat(),A.a[0][1]=A.a[1][0]=A.a[1][1]=B.a[0][0]=C.a[0][0]=C.a[1][1]=1,B.a[1][0]=2;x;x>>=1LL,A=A*A)if(x&1LL)C=C*A;

    C=C*B;

    printf("%lld\n",C.a[0][0]);

  }

  return 0;

}

  

 

你可能感兴趣的:(ZOJ)