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;
}

 

posted @ 2018-04-11 00:10  Zinn  阅读(183)  评论(0编辑  收藏  举报