• bzoj1799同类分布——数位DP


    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1799

    数位DP。

    1、循环方法

    预处理出每个位数上,和为某个数,模某个数余某个数的所有情况;

    因为开四维会爆空间,所以省去模数,为此需要固定模数一次一次累加;

    余数的转移,以及可以填数的范围都值得注意。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    ll l,r,mul[22],f[22][205][205],ans,sx;
    int a[3][22],w[3];
    void cl(int t,ll tmp)
    {
        while(tmp)
        {
            a[t][++w[t]]=tmp%10;
            tmp/=10;
        }
    }
    void pw()
    {
        mul[0]=1;
        for(int i=1;i<w[1];i++)//<
            mul[i]=mul[i-1]*10;
    }
    void pre(int s)
    {
        memset(f,0,sizeof f);
        f[0][0][0]=1;
        ll lm=0;
        for(int i=1;i<w[1];i++)//<
        {
            lm=min(s,i*9);
            for(int j=0;j<=lm;j++)
                for(int l=0;l<s;l++)
                    for(int p=0;p<=9&&j-p>=0;p++)
    //                    f[i][j][l]+=f[i-1][j-p][((s-(l-p*mul[i-1]))%s+s)%s];
                        f[i][j][l]+=f[i-1][j-p][((l-p*mul[i-1])%s+s)%s];
        }
    }
    ll cal(int t,int s)
    {
        ll cnt=0,lj=0;
        int ss=0;
        for(int i=w[t];i;i--)
        {
            int lm=a[t][i];
            if(i==1)lm++;
            for(int p=0;p<lm&&s-ss-p>=0;p++)
                cnt+=f[i-1][s-ss-p][(s-(lj+p*mul[i-1])%s)%s];//
            ss+=a[t][i];lj+=a[t][i]*mul[i-1];
        }
        return cnt;
    }
    int main()
    {
        scanf("%lld%lld",&l,&r);
        cl(0,l-1);cl(1,r);
        pw();
        sx=(w[1]-1)*9+a[1][w[1]];
        for(int s=1;s<=sx;s++)
        {
            pre(s);
            ans+=cal(1,s)-cal(0,s);
        }
        printf("%lld",ans);
        return 0;
    }

    2、递归方法

    记忆化,代码极简单,详见注释。

    代码如下:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    typedef long long ll;
    int a[25],mod,cnt,tot;
    ll f[3][25][205][205],vis[3][25][205][205],x,y;//tot时间戳 
    ll dp(int p,int d,int s,int v)//p为有无限制,d为第几位,s为位数和,v为模余数
    {
        if(!d)return (!s&&!v);//边界 
        if(vis[p][d][s][v]==tot)return f[p][d][s][v];//记忆化
        vis[p][d][s][v]=tot;
        ll l=max(0,s-(d-1)*9),r=min(s,((p)?a[d]:9));//
        ll cnt=0;
        for(int i=l;i<=r;i++)
             cnt+=dp((p&(i==a[d])),d-1,s-i,(10*v+i)%mod);//%mod而非%s 
        return f[p][d][s][v]=cnt;
    } 
    ll solve(ll x)
    {
        for(cnt=0;x;x/=10)a[++cnt]=x%10;
        ll tmp=0;
        for(mod=1;mod<=cnt*9;mod++)//cnt*9而非a[cnt]*9 
            tot++,tmp+=dp(1,cnt,mod,0);//一开始就有限制 
        return tmp;
    }
    int main()
    {
        scanf("%lld%lld",&x,&y);
        printf("%lld",solve(y)-solve(x-1));
        return 0;
    }
  • 相关阅读:
    linux执行命令并获取结果(system)
    awk----基本用法
    shell用法总结
    autoit(au3)使用说明
    博客搜集
    vi常用快捷键总结
    python简单学------------程序传参数,列表推导式,set、list、tuple 转换
    python简单学------------模块
    python简单学------------python面向对象(3)
    python简单学------------python面向对象(2)
  • 原文地址:https://www.cnblogs.com/Zinn/p/8784936.html
Copyright © 2020-2023  润新知