[BZOJ1799][AHOI2009]同类分布 数位DP+记忆化搜索

考虑枚举数位之和m。对于每个m,问题简化为有多少个数数位之和==m,且%m=0。设计状态f[v][s][r][0/1]表示从高到低填到第v位,当前和为s,%m=r,未填满/填满的方案数。
TLE的DP,初始状态为f[19][0][0][1]=1:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#define ll long long
using namespace std;
ll f[20][170][170][2],mi[20];
ll solve(ll n,int mod)
{
    memset(f,0,sizeof(f));
    f[19][0][0][1]=1;
    for(register int i=19;i>=1;i--)
    {
        int t=n/mi[i-1]%10,dw=max(mod-9*i,0);
        for(register int j=0;j<=9;j++)
            for(register int s=dw;s<=mod-j;s++)
                for(register int r=0;r<mod;r++)
                {
                    int cs=s+j,cr=((r<<3)+(r<<1)+j)%mod;
                    f[i-1][cs][cr][0]+=f[i][s][r][0];
                    if(j<t) f[i-1][cs][cr][0]+=f[i][s][r][1];
                    else if(j==t) f[i-1][cs][cr][1]+=f[i][s][r][1]; 
                }   
    }

    return f[0][mod][0][0]+f[0][mod][0][1];
}
int main()
{
    ll a,b,ans=0;
    scanf("%lld%lld",&a,&b);
    mi[0]=1;
    for(int i=1;i<=18;i++) mi[i]=mi[i-1]*10;
    for(int i=1;i<=162;i++)
        ans+=solve(b,i)-solve(a-1,i);
    printf("%lld\n",ans);   
    return 0;
}

AC的记忆化搜索,终止状态为f[0][m][0][0/1]=1(思路要反过来一下):

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
ll f[20][170][170][2];
int a[20],cnt=0,tot,mark[20][170][170][2];
ll dp(int v,int s,int r,int b)
{
    if(v==0) return ((s==tot)&&(r==0)) ;
    if(mark[v][s][r][b]==tot) return f[v][s][r][b];
    mark[v][s][r][b]=tot;ll t=0;
    int lx=max(0,tot-s-(v-1)*9),rx=min((b)?a[v]:9,tot-s); 
    for(int i=lx;i<=rx;i++) t+=dp(v-1,s+i,(r*10+i)%tot,b&(i>=a[v]));
    return (f[v][s][r][b]=t);
}
ll solve(ll x)
{
    memset(mark,0,sizeof(mark));
    ll re=0;cnt=0;
    for(;x;x/=10) a[++cnt]=x%10;
    for(tot=1;tot<=cnt*9;tot++)
        re+=dp(cnt,0,0,1);
    return re;  
}
int main()
{
    ll x,y;
    scanf("%lld%lld",&x,&y);
    printf("%lld\n",solve(y)-solve(x-1));   
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值