zoukankan      html  css  js  c++  java
  • 数位DP总结

    a positive integer number is beautiful if and only if it is divisible by each of its nonzero digits.
        问一个区间内[l,r]有多少个Beautiful数字
        范围9*10^18

    思路一般,但是取模以及映射也是非常玄妙的

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define ll long long
    //dp[i][j][k][sta] means the first i nomber,whether it has limit,the val%2520,the lcm of ezch number
    ll pow[22],dp[22][2][2520][50];
    int num[22],flcm[512],Map[2520],Map1[50],tmp[22],tot;
    int gcd(int a,int b)
    {
        if(!b) return a;
        return gcd(b,a%b);
    }
    int lcm(int a,int b)
    {
        if(a==0) return b;
        if(b==0) return a;
        return a*b/gcd(a,b);
     } 
    void init()
    {
        pow[0]=1;
        for(int i=1;i<=18;i++)
        {
            pow[i]=pow[i-1]*10%2520;
        }
        for(int i=1;i<(1<<9);i++)
            for(int j=1;j<=9;j++)
                if(i&(1<<j-1)) flcm[i]=lcm(flcm[i-(1<<j-1)],j);
        for(int i=1;i<=(1<<9);i++)
            if(!Map[flcm[i]]) Map1[++tot]=flcm[i],Map[flcm[i]]=tot;    
    }
    ll solve(ll x)
    {
        int len=0;
        ll ans=0;
        while(x) tmp[++len]=x%10,x/=10;
        for(int i=1;i<=len;i++)
        num[i]=tmp[len-i+1]; 
        memset(dp,0,sizeof(dp));
        dp[0][1][0][1]=1;
        for(int i=0;i<=len;i++)
            for(int j=0;j<2;j++)
                for(int k=0;k<2520;k++)
                    for(int l=1;l<=tot;l++)
                    {
                        ll p=dp[i][j][k][l];
                        int pp=Map1[l];
                        if(!p) continue;
                        if (i==len) 
                        {
                            if (k % pp==0) ans+=p;
                            continue;
                        }
                        if(!j)
                        {
                            for(int q=0;q<=9;q++)
                            {
                                dp[i+1][0][(k+q*pow[len-i-1])%2520][Map[lcm(pp,q)]]+=p;
                            }
                        }else
                        {
                            for(int q=0;q<num[i+1];q++)
                            {
                                dp[i+1][0][(k+q*pow[len-i-1])%2520][Map[lcm(pp,q)]]+=p;
                            }
                            dp[i+1][1][(k+num[i+1]*pow[len-i-1])%2520][Map[lcm(pp,num[i+1])]]+=p;
                        }
                    }
        return ans;            
    }
    int main()
    {
        init();
        int cas;
        scanf("%d",&cas);
        while(cas--)
        {
            ll a,b;
            scanf("%lld %lld",&a,&b); 
            printf("%lld
    ",solve(b)-solve(a-1));
        }
     } 
    杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer)。 
    杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。 
    不吉利的数字为所有含有4或62的号码。例如: 
    62315 73418 88914 
    都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。 
    你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。 

    Input输入的都是整数对n、m(0<n≤m<1000000),如果遇到都是0的整数对,则输入结束。 
    Output对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。 
    Sample Input

    1 100
    0 0

    Sample Output

    80
    水体不解释
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int dp[25][2][2],tmp[25],num[25];
    int solve(int x)
    {
        int len=0,ans=0;
        while(x) tmp[++len]=x%10,x/=10;
        for(int i=1;i<=len;i++)
            num[len-i+1]=tmp[i];    
        memset(dp,0,sizeof(dp));    
        dp[0][1][0]=1;
        for(int i=0;i<=len;i++)
            for(int j=0;j<2;j++)
                for(int k=0;k<2;k++)
                {
                    int pp=dp[i][j][k];
                    if(!pp) continue;
                    if(i==len)
                        ans+=pp;
                    if(!j)
                    {
                        if(!k)
                        {
                            for(int q=0;q<=9;q++)
                            if(q!=6&&q!=4) dp[i+1][0][0]+=pp;else if(q!=4)dp[i+1][0][1]+=pp;
                        }else
                        for(int q=0;q<=9;q++)
                        if(q!=6&&q!=2&&q!=4) dp[i+1][0][0]+=pp;else if(q==6) dp[i+1][0][1]+=pp; 
                    }else
                    {
                        if(!k)
                        {
                            for(int q=0;q<num[i+1];q++)
                            if(q!=6&&q!=4) dp[i+1][0][0]+=pp;else if(q!=4)dp[i+1][0][1]+=pp;
                            
                            if(num[i+1]!=6&&num[i+1]!=4) dp[i+1][1][0]+=pp;else if(num[i+1]!=4)dp[i+1][1][1]+=pp;
                        }else
                        {
                            for(int q=0;q<num[i+1];q++)
                            if(q!=6&&q!=2&&q!=4) dp[i+1][0][0]+=pp;else if(q==6) dp[i+1][0][1]+=pp;
                            if(num[i+1]!=6&&num[i+1]!=2&&num[i+1]!=4) dp[i+1][1][0]+=pp;else if(num[i+1]==6) dp[i+1][1][1]+=pp;
                        }
                    }
                }
        return ans;             
    }
    int main()
    {
        int a,b;
        while(~scanf("%d %d",&a,&b)&&(a||b))
        {
            printf("%d
    ",solve(b)-solve(a-1));
        }
     } 

     还有几道题

    C - Bomb

     Solution:同上

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define ll long long
    using namespace std;
    ll tmp[55],num[55],len,dp[55][2][2];
    ll solve(ll x)
    {
        len=0;
        while(x)
        {
            tmp[++len]=x%10;
            x/=10;
        }
        memset(dp,0,sizeof(dp));
        for(ll i=1;i<=len;i++)
        num[len-i+1]=tmp[i];
        dp[0][1][0]=1;
        ll ans=0;
        for(ll i=0;i<=len;i++)
        {
            for(ll j=0;j<2;j++)
            {
                for(ll k=0;k<2;k++)
                {
                    ll pp=dp[i][j][k];
                    if(i==len)
                    {
                        ans+=pp;
                        continue;
                    }
                    if(!pp) continue;
                    if(!j)
                    {
                        if(!k)
                        {
                            for(ll q=0;q<=9;q++)
                            if(q!=4)
                            dp[i+1][0][0]+=pp;else dp[i+1][0][1]+=pp;
                        }else
                        for(ll q=0;q<=8;q++)
                        {
                            if(q==4) dp[i+1][0][1]+=pp;else dp[i+1][0][0]+=pp;
                        }
                    }else
                    {
                        if(!k)
                        {
                            for(ll q=0;q<num[i+1];q++)
                            {
                                if(q==4)
                                {
                                    dp[i+1][0][1]+=pp;
                                }else dp[i+1][0][0]+=pp;
                            }
                            if(num[i+1]==4) dp[i+1][1][1]+=pp;else dp[i+1][1][0]+=pp;
                        }else
                        {
                            for(ll q=0;q<num[i+1];q++)
                            if(q!=9)
                            {
                                if(q==4)
                                {
                                    dp[i+1][0][1]+=pp;    
                                }else dp[i+1][0][0]+=pp;
                            }
                            if(num[i+1]!=9)
                            {
                                if(num[i+1]==4)
                                {
                                    dp[i+1][1][1]+=pp;
                                }else dp[i+1][1][0]+=pp;
                            }
                         } 
                    }
                }
            }
        }
        return ans;
    }
    int main()
    {
        ll cas,n;
        scanf("%lld",&cas);
        while(cas--)
        {
            scanf("%lld",&n);
            printf("%lld
    ",n-solve(n)+1);
        }
    }

    D - Round Numbers

     第一个用dfs写的题
    #include<cstdio>
    #include<cstring>
    #define ll long long
    using namespace std;
    int dp[40][40][40],num[40];
    ll dfs(ll pos,ll num0,ll num1,bool flag,bool z)
    {
        if(pos==-1) return num0>=num1;
        if(!flag&&dp[pos][num0][num1]!=-1) return dp[pos][num0][num1];
        ll maxn=flag?num[pos]:1;
        ll ans=0;
        for(ll i=0;i<=maxn;i++)
            ans+=dfs(pos-1,(z&&i==0)?0:num0+(i==0),(z&&i==0)?0:num1+(i==1),flag&&i==maxn,z&&i==0);
        if(!flag) dp[pos][num0][num1]=ans;
        return ans;
    }
    ll cal(ll x)
    {
        ll pos=0;
        while(x)
        {
            num[pos++]=x%2;
            x/=2;
        }
        return dfs(pos-1,0,0,1,1);
    }
    int main()
    {
        ll n,m;
        memset(dp,-1,sizeof(dp));
       scanf("%lld%lld",&n,&m);
            printf("%lld
    ",cal(m)-cal(n-1));
        return 0;
    }

    E - Balanced Number

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define ll long long
    using namespace std;
    ll l,r;
    ll tmp[25],num[25],f[25][25][2005];
    ll dfs(ll pos,ll x,ll sum,ll limit)
    {
        if(!pos) return sum==0;
        if(sum<0) return 0;
        ll ans=0;
        if(!limit&&~f[pos][x][sum]) return f[pos][x][sum];
        ll up=limit? num[pos]:9;
        for(ll i=0;i<=up;i++)
        {
            ans+=dfs(pos-1,x,sum+(i)*(pos-x),limit&&(i==up));
        }
        if(!limit) f[pos][x][sum]=ans;
        return ans;
    }
    ll solve(ll x)
    {
        ll len=0;
        while(x)
        {
            tmp[++len]=x%10;
            x/=10;
        }
        ll sum=0;
        for(ll i=1;i<=len;i++)    num[i]=tmp[i];
        for(ll i=1;i<=len;i++) sum+=dfs(len,i,0,1);
        return sum-len+1;
    }
    int main()
    {
        ll T;
        memset(f,-1,sizeof(f));
        scanf("%lld",&T);
        while(T--)
        {
            scanf("%lld %lld",&l,&r);
            printf("%lld
    ",solve(r)-solve(l-1));
         } 
     } 

    bzoj1388

    一不小心先打成爆艘了,后来改成记忆化

    F - B-number

     

     好题啊,其实就是上面两题的组合

    有个容斥的思想

    可以被13整除的数且含13的数的数量等于 (所有的数的数量-不是13倍数的数)-是13倍数又不能含13的数

    括号中的答案可以化简为n/13

    剩下的就是数位Dp的模板了

    Niro BC说记忆化垃圾,但是有些情况Dp真的麻烦,就两个条件就长成这鸟样%%

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int tmp[50],num[50],dp[50][2][2][13],ans,n;
    int solve(int n)
    {
        memset(dp,0,sizeof(dp));
        int len=0;
        while(n)
        {
            tmp[++len]=n%10;
            n/=10;
        }
        int ans=0;
        for(int i=1;i<=len;i++) num[len-i+1]=tmp[i];
        dp[0][1][0][0]=1;
        for(int i=0;i<=len;i++)
            for(int j=0;j<2;j++)
                for(int k=0;k<2;k++)
                    for(int sta=0;sta<13;sta++)
                    {
                        int pp=dp[i][j][k][sta];
                        if(!pp) continue;
                        if(i==len) if(sta==0) ans+=pp;
                        if(!j)
                        {
                            if(!k)
                            {
                                for(int q=0;q<=9;q++)
                                if(q!=1)
                                dp[i+1][0][0][(sta*10+q)%13]+=pp;else dp[i+1][0][1][(sta*10+q)%13]+=pp;
                            }else
                            for(int q=0;q<=9;q++)
                            if(q!=3)
                            {
                                if(q!=1)
                                dp[i+1][0][0][(sta*10+q)%13]+=pp;else dp[i+1][0][1][(sta*10+q)%13]+=pp;
                            }
                        }else
                        {
                            if(!k)
                            {
                                for(int q=0;q<num[i+1];q++)
                                if(q!=1) dp[i+1][0][0][(sta*10+q)%13]+=pp;else dp[i+1][0][1][(sta*10+q)%13]+=pp;
                                if(num[i+1]!=1) dp[i+1][1][0][(sta*10+num[i+1])%13]+=pp;else dp[i+1][1][1][(sta*10+num[i+1])%13]+=pp; 
                            }else
                            {
                                for(int q=0;q<num[i+1];q++)
                                if(q!=3)
                                {
                                    if(q!=1)
                                    dp[i+1][0][0][(sta*10+q)%13]+=pp;else dp[i+1][0][1][(sta*10+q)%13]+=pp;
                                }
                                if(num[i+1]!=3)
                                {
                                    if(num[i+1]!=1) dp[i+1][1][0][(sta*10+num[i+1])%13]+=pp;else dp[i+1][1][1][(sta*10+num[i+1])%13]+=pp;
                                }
                            }
                        }
                    } 
            return ans;        
    }
    int main()
    {
        while(~scanf("%d",&n))
        {
            printf("%d
    ",n/13-solve(n)+1);
        }
     } 
  • 相关阅读:
    ExtJS 刷新或者重载Tree后,默认选中刷新前最后一次选中的节点代码片段
    ios>APP名称的多语言化(转)
    android>apk破解以及重新编译(转)
    MFC动态库基本概念
    (内存中的)堆和栈的区别(转过无数次的文章)
    面向对象五大基本原则
    VS20052008程序发布、打包(MFC)
    在MFC中创建动态控件的生成与响应
    SQL2000自动备份数据库并发送邮件报告数据库自动备份情况
    The Concept of Callbacks
  • 原文地址:https://www.cnblogs.com/dancer16/p/7719808.html
Copyright © 2011-2022 走看看