【BZOJ】1833 [ZJOI2010]count 数字计数

【算法】数位DP

【题解】

记忆化搜索

#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
ll A[10],B[10],f[20][11],a[20],p[10];
ll dfs(ll* A,ll h,bool limit,bool pre)
{
    if(h==0)return 1;
    if(!limit&&f[h][10]!=-1&&!pre)
     {
         for(int i=0;i<=9;i++)A[i]+=f[h][i];
         return f[h][10];
     }
    int end=limit?a[h]:9;ll ans=0;
    for(int i=0;i<=end;i++)
     {
         if(limit&&i==end)p[i]=dfs(A,h-1,1,pre&(!i)),A[i]+=p[i],ans+=p[i];
          else p[i]=dfs(A,h-1,0,pre&(!i)),A[i]+=p[i],ans+=p[i];
         if(pre&(!i))A[i]-=p[i];
     }
    if(!limit&&!pre)
     {
         for(int i=0;i<=9;i++)f[h][i]=f[h-1][i]*10+p[i];
         f[h][10]=ans;
     }
    return ans;
}
void solve(ll* A,ll x)
{
    if(x==0){return;}
    int n=0;
    while(x>0)a[++n]=x%10,x/=10;
    dfs(A,n,1,1);
}
int main()
{
    ll l,r;
    scanf("%lld%lld",&l,&r);
    for(int i=0;i<=20;i++)f[i][10]=-1;
    solve(B,l-1);solve(A,r);
    for(int i=0;i<9;i++)printf("%lld ",A[i]-B[i]);
    printf("%lld\n",A[9]-B[9]);
    return 0;
}
View Code

递推:下面只考虑单一数字数量统计,其它一样。

第一步,预处理。(计数)

规定最低位为第1位,最高位为第len位。

f[i][j]表示前i位,最高位数字为j的答案数(不考虑前导零有前导零的数字只要待会再最高位附上数字就是有效的了)

f[i][j]=∑f[i-1][k]+10^(i-1)。

第二步,第len位为0。(前导零)

将所有len位为0的或数字长度不足len的数字先统计进来,ans+=∑f[i][j]+1,1<=i<=len-1,1<=j<=9。其中+1是数字0。

第三步,第len位不为0逐位确定。(限位)

强制确定len位不为0,然后加入每一位枚举到顶-1的答案就可以了。

ans+=∑f[i][j],1<=i<len,0<=j<=a[i],其中i=len时j从1开始。

还要计算当前数位的顶之后会出现的次数,为后面数字大小+1。

例如1211这个数字,整个数位DP的过程是:0,1~9,10~99,100~999,1000~1199,1200~1209,1210~1211。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=16,M=20;
int a[M];
ll fac[M];
struct cyc{
    ll a[10];
}f[N][N];
cyc operator + (cyc a,cyc b){
    cyc c;
    for(int i=0;i<=9;i++)c.a[i]=a.a[i]+b.a[i];
    return c;
}
cyc dp(ll num){
    int len=0;
    cyc ans;
    for(int i=0;i<=9;i++)ans.a[i]=0;
    ans.a[0]=1;
    if(!num)return ans;
    ll number=num;
    while(num){
        a[++len]=num%10;
        num/=10;
    }
    for(int i=1;i<len;i++)for(int j=1;j<=9;j++)ans=ans+f[i][j];
    for(int i=len;i>=1;i--){
        for(int j=(i==len);j<a[i];j++){
            ans=ans+f[i][j];
        }
        number%=fac[i-1];
        ans.a[a[i]]+=number+1;
    }
    //for(int k=0;k<=9;k++)printf("%d ",ans.a[k]);puts("");
    return ans;
}
int main(){
    fac[0]=1;for(int i=1;i<=N;i++)fac[i]=fac[i-1]*10;
    for(int j=0;j<=9;j++)f[1][j].a[j]=1;
    for(int i=2;i<=N;i++){
        for(int j=0;j<=9;j++){
            for(int k=0;k<=9;k++){
                f[i][j]=f[i][j]+f[i-1][k];
                f[i][j].a[j]+=fac[i-2];
            }//printf("[%d][%d]",i,j);
            //for(int k=0;k<=9;k++)printf("%d ",f[i][j].a[k]);puts("");
        }
    }
    ll A,B;scanf("%lld%lld",&A,&B);
    cyc cA=dp(A-1),cB=dp(B);
    for(int i=0;i<9;i++)printf("%lld ",cB.a[i]-cA.a[i]);
    printf("%lld",cB.a[9]-cA.a[9]);
    return 0;
}
View Code

 

posted @ 2017-10-19 18:00  ONION_CYC  阅读(260)  评论(0编辑  收藏  举报