1. 程式人生 > >[ZJOI2010]數字計數,數位Dp

[ZJOI2010]數字計數,數位Dp

正題

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