[ZJOI2010]数字计数,数位Dp

版权声明:因为我是蒟蒻,所以请大佬和神犇们不要转载(有坑)的文章,并指出问题,谢谢 https://blog.csdn.net/Deep_Kevin/article/details/83002946

正题

      [ZJOI2010]数字计数

      这题看上去就像数位Dp.

      对于每一个数,我们进行数位Dp。

      还是类似于差分,用y的答案减去x-1的答案就可以了。

      对于每个子问题,我们求解,用tf来记录前面是否“满”,用all记录前面是否都是0.

      如果前面满了,我们枚举的end就是当前这一位的值,否则就是9.

      如果这一位以及前面有不是0的位,那么这一位就有可能能取到答案,前面以及这一位满了,那么就是后一位的权值+1,否则就可以取10的幂次方。

      这样每次Dp都记忆化一下就可以了。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;

long long x,y;
long long solve[100];
long long val[100];
long long ci[100];
long long g[100];

long long Dp(int pos,int tf,int k,int all){
    if(pos==0) return 0;
    if(!tf && !all && solve[pos]!=-1) return solve[pos];
    long long res=0;
    int end=tf?val[pos]:9;
    for(int i=0;i<=end;i++)	{
        res+=Dp(pos-1,tf&&i==end,k,all&&i==0);
        if(!(all && i==0) && i==k) res+=((i==end&&tf)?g[pos-1]+1:ci[pos-1]);
    }
    if(!all && !tf) solve[pos]=res;
    return res;
}

long long gett(long long v,int k){
    memset(solve,-1,sizeof(solve));
    int len=0;
    while(v!=0){
        val[++len]=v%10;
        v/=10;
        g[len]=g[len-1]+val[len]*ci[len-1];
    }
    return Dp(len,1,k,1);
}

int main(){
    ci[0]=1;
    for(int i=1;i<=15;i++) ci[i]=ci[i-1]*10;
    long long tot=0;
    scanf("%lld %lld",&x,&y);
    x--;
    for(int i=0;i<=9;i++)
        printf("%lld ",gett(y,i)-gett(x,i));
}

猜你喜欢

转载自blog.csdn.net/Deep_Kevin/article/details/83002946