• 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("
    ");
        }
        //f[2][0][0].output();printf("
    ");f[2][1][0].output();printf("
    ");
        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
    ",i,g[i][0]);printf("pre:");Ans.output();printf("
    ");
                    Ans=Ans+f[m-i-1][0][0];Ans=Ans+f[m-i-1][1][0];//printf("after:");Ans.output();printf("
    ");
                    
                }
                if(g[i][calc(0,1)]==ans){//printf("B%d
    ",i);printf("pre:");Ans.output();printf("
    ");
                    Ans=Ans+f[m-i-1][0][1];Ans=Ans+f[m-i-1][1][1];//printf("after:");Ans.output();printf("
    ");
                }
                
            }
        }
        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("
    ");
        }//while(1);
       // fclose(stdin);fclose(stdout);
        return 0;
    }

     

  • 相关阅读:
    一个界面描述标签的想法
    使用MyGeneration创建模板:介绍(翻译)
    ICE:C#和Java共同的服务器
    Spring.NET 快速入门 (翻译)
    TSQL命令在SQLServer查询中的运用
    SQL数据库修复
    区分汉字和字母的函数
    如何生成静态页
    delphi 开发扩展(一)
    20072008
  • 原文地址:https://www.cnblogs.com/liu-runda/p/6393765.html
Copyright © 2020-2023  润新知