bzoj3780 数字统计

题意:

将[L..R]中的所有整数用M位二进制数表示(允许出现前导0)现在将这些数中的每一个作如下变换:

从这个数的最低两位开始,如果这两位都是0,那么X=1,否则X=0。现在将这两位删去,然后将X放在原来最低位的位置上。重复这个变换直到这个数只剩下一位为止。

例如01001的变换过程:01001-->0100-->011-->00-->1。
问[L,R]中多少个数变换后值为Y(Y为0或1),用无前导零的二进制数输出
 1<=M<=200,1<=数据组数<=50。

这道题的性质很特殊所以可以用一些机智的方法过掉,但懒于观察性质就可以写数位DP.

关于数位DP:

一般是要求将数字视为字符串并统计信息,并通常要求计算一段数字区间的信息的和的问题。

数据范围常能达到 10^9 或者 10^18。

特点:思想一般比较简单但是细节较多,不对拍基本上是要完.

数位DP常见的是在十进制数字串上进行,本题是二进制串,也可使用相同的思路.

首先区间查询可以转化为前缀和相减,我们只需要解决[0,L-1]和[0,R]这两个区间的答案即可.

那么问题变成有多少小于等于x的数满足合并的最终结果为0/1

对于等于x的情况我们特判一下,接下来考虑小于x的情况.如果一个数y小于x那么必然有一位和x不同,且一定是x的这一位为1,y的这一位为0.而且,y在这个位置后面的数位随意填写都可以满足y<x

例如,x为01101,y的前三位为010,那么y后面的两位任意填写都可以使y<x

那么我们枚举y从左到右第一个和x不同的位置在哪里.这个位置必须满足x的这一位是1且y的这一位是0.假如后面还有i个位置,那么这i个数位可以随意填写,我们可以预处理出有多少个长度为i的二进制串合并之后得到的结果为0/1,记为f[i][0]和f[i][1].(预处理的时候需要加一维才能转移,考虑一下)

对于左侧的数位,y和x是一样的,我们可以预处理出x的左边i位在右边加上一个0/1之后合并得到的结果,然后就可以DP了.写起来会有一些细节,一定要对拍.

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=205;
struct ll{
    int num[maxn];int len;
    ll(){
        len=0;memset(num,0,sizeof(num));
    }
    ll(int x){
        memset(num,0,sizeof(num));len=1;num[1]=x;
        while(num[len]>=2){
            num[len+1]=num[len]/2;num[len]%=2;len++;
        }
    }
    ll operator +(const ll &B)const{
        ll C;C.len=max(len,B.len);
        for(int i=1;i<=C.len;++i)C.num[i]=num[i]+B.num[i];
        for(int i=1;i<=C.len;++i){
            if(C.num[i]>=2){
                C.num[i+1]+=C.num[i]/2;C.num[i]%=2;
                if(i==C.len)C.len++;
            }
        }
        return C;
    }
    ll operator -(const ll &B)const{//assert:C>0
        ll C;C.len=len;
        for(int i=1;i<=len;++i)C.num[i]=num[i]-B.num[i];
        for(int i=1;i<=len;++i){
            if(C.num[i]<0){
                C.num[i]+=2;C.num[i+1]--;
            }
        }
        while(C.num[C.len]==0&&C.len>=1)C.len--;
        return C;
    }
    void output(){
        if(len==0){
            printf("0");return;
        }
        for(int i=len;i>=1;--i)printf("%d",num[i]);
    }
    void operator +=(const ll &B){
        (*this)=(*this)+B;
    }
}ANS;
int m,ans;
char str1[maxn],str2[maxn];
int g[maxn][2];ll f[maxn][2][2];
inline int calc(const int &a,const int &b){
    return (a==b)&&(a==0);
}
ll dp(char s[]){
    g[0][0]=0;g[0][1]=1;
    for(int i=0;i<m;++i){
        g[i+1][0]=g[i][calc(0,s[i]-'0')];g[i+1][1]=g[i][calc(1,s[i]-'0')];
    }
    memset(f,0,sizeof(f));
    f[1][0][0]=f[1][1][1]=ll(1);
    for(int i=1;i<m;++i){
        f[i+1][0][1]+=f[i][0][0];f[i+1][0][1]+=f[i][1][0];
        f[i+1][0][0]+=f[i][0][1];f[i+1][0][0]+=f[i][1][1];
        f[i+1][1][0]+=f[i][0][0];f[i+1][1][0]+=f[i][1][0];
        f[i+1][1][0]+=f[i][0][1];f[i+1][1][0]+=f[i][1][1];
        //f[i+1][0][1].output();printf("\n");
    }
    //f[2][0][0].output();printf("\n");f[2][1][0].output();printf("\n");
    ll Ans=0;
    for(int i=0;i<m;++i){
        if(i==m-1){
           if(g[i][s[i]-'0']==ans){Ans+=ll(1);}
           if(s[i]=='1'&&g[i][0]==ans){Ans+=ll(1);}
        }
        else if(s[i]=='1'){
            if(g[i][calc(0,0)]==ans){//printf("A%d %d\n",i,g[i][0]);printf("pre:");Ans.output();printf("\n");
                Ans=Ans+f[m-i-1][0][0];Ans=Ans+f[m-i-1][1][0];//printf("after:");Ans.output();printf("\n");
                
            }
            if(g[i][calc(0,1)]==ans){//printf("B%d\n",i);printf("pre:");Ans.output();printf("\n");
                Ans=Ans+f[m-i-1][0][1];Ans=Ans+f[m-i-1][1][1];//printf("after:");Ans.output();printf("\n");
            }
            
        }
    }
    return Ans;
}
bool check(char s[]){
    int t=s[m-1]-'0';
    for(int i=m-2;i>=0;--i)t=calc(t,s[i]-'0');
    return t==ans;
}
int main(){
   // freopen("count.in","r",stdin);
   // freopen("count.out","w",stdout);
    int t;scanf("%d",&t);
    while(t--){
        scanf("%d%d",&m,&ans);
        scanf("%s%s",str1,str2);
        ANS=dp(str2)-dp(str1);
        if(check(str1))ANS=ANS+ll(1);
        ANS.output();printf("\n");
    }//while(1);
   // fclose(stdin);fclose(stdout);
    return 0;
}

 

 

posted @ 2017-02-13 14:52  liu_runda  阅读(280)  评论(0编辑  收藏  举报
偶然想到可以用这样的字体藏一点想说的话,可是并没有什么想说的. 现在有了:文化课好难