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);
        }
     } 
  • 相关阅读:
    监控里的主码流和子码流是什么意思
    监控硬盘容量计算
    一个能让你了解所有函数调用顺序的Android库
    电工选线
    oracle linux dtrace
    list all of the Oracle 12c hidden undocumented parameters
    Oracle Extended Tracing
    window 驱动开发
    win7 x64 dtrace
    How to Use Dtrace Tracing Ruby Executing
  • 原文地址:https://www.cnblogs.com/dancer16/p/7719808.html
Copyright © 2011-2022 走看看