BZOJ3329:Xorequ——题解

http://www.lydsy.com/JudgeOnline/problem.php?id=3329

原式化为x^2x=3x,而且实际上异或就是不进位的加法。

那么我们又有x+2x=3x,所以在做加法的时候也没有(在二进制中)进位。

由此我们得到:x必须(在二进制下)没有相邻的1。

那么第一问我们可以采用数位dp,相信看这篇博客的你一定会了数位dp,不会的话设f[i][j][0/1]表示第i位放j数,且前i位比n的前i位小于等于/大于。

剩下的就去学数位dp吧。

第二问就相当于问符合条件的长度为n的01串个数,可以打表,也可以感性证明一下发现就是fib的第n+2项。

于是愉快的矩阵乘法。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll p=1e9+7;
ll f[100][2][2];
int q[100];
ll dp(ll x){
    int len=0;
    while(x)q[++len]=x%2,x/=2;
    if(len==0)q[++len]=0;
    memset(f,0,sizeof(f));
    for(int i=0;i<=1;i++){
        if(i<=q[1])f[1][i][0]=1;
        else f[1][i][1]=1;
    }
    for(int i=2;i<=len;i++){
        for(int j=0;j<=1;j++){
            for(int k=0;k<=1;k++){
                if(j+k!=2){
                    if(j<q[i])
                        f[i][j][0]+=f[i-1][k][0]+f[i-1][k][1];
                    else if(j==q[i])
                        f[i][j][0]+=f[i-1][k][0],f[i][j][1]+=f[i-1][k][1];
                    else f[i][j][1]+=f[i-1][k][0]+f[i-1][k][1];
                }
            }
        }
    }
    ll ans=f[len][1][0];
    for(int i=len-1;i;i--)ans+=f[i][1][0]+f[i][1][1];
    return ans;
}
struct node{
    ll g[2][2];
};
void buildI(node &a){
    for(int i=0;i<2;i++){
        for(int j=0;j<2;j++){
            a.g[i][j]=(i==j);
        }
    }
}
void multi(node x,node y,node &z){
    memset(z.g,0,sizeof(z.g));
    for(int i=0;i<2;i++){
        for(int j=0;j<2;j++){
            if(x.g[i][j]){
                for(int l=0;l<2;l++){
                    z.g[i][l]+=x.g[i][j]%p*y.g[j][l]%p;
                    z.g[i][l]%=p;
                }
            }
        }
    }
    return;
}
node a,b;
void qpow(ll k){
    buildI(a);
    while(k){
        if(k&1)multi(a,b,a);
        multi(b,b,b);
        k>>=1;
    }
    return;
}
ll solve(ll n){
    b.g[0][0]=1;b.g[0][1]=1;
    b.g[1][0]=1;b.g[1][1]=0;
    qpow(n+2);
    return a.g[0][1]%p;
}
ll t,n;
int main(){
    scanf("%lld",&t);
    while(t--){
        scanf("%lld",&n);
        printf("%lld\n%lld\n",dp(n),solve(n));
    }
    return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

posted @ 2018-02-02 22:29  luyouqi233  阅读(207)  评论(0编辑  收藏  举报