[BZOJ 1833] 数字计数

Link:https://www.lydsy.com/JudgeOnline/problem.php?id=1833

Algorithm:

比较明显的数位DP

先预处理出1~9和包括前导0的0的个数:pre[i]=pre[i-1]*10^(digit-1)(可以分为首位和其它位)

接下来求(L,R)的个数,可以用(1,R)-(1,L-1)差分来做

在求(1,K)时,我们先根据预处理的值算出[0,999....99]的值

接下来再一位一位地算出有边界的值即可

#include <bits/stdc++.h>  
using namespace std;  
typedef long long ll;  
ll res[10],pre[20];  

void split(ll x,ll pos){while(x) res[x%10]+=pos,x/=10;}

void Digital_DP(ll x,int flag)  
{  
    int i,j;  
    ll pos=10,now;  
    for(i=1;pos<x;i++)  
    {  
        for(j=0;j<=9;j++)  
            res[j]+=pre[i-1]*9*flag;  
        for(j=1;j<=9;j++)  
            res[j]+=pos/10*flag;
        pos*=10;
    }  
    
    now=pos/=10;i--; 
    while(now<x)  
    {  
        while(now+pos<=x)  
        {  
            ll temp=now/pos;  
            split(temp,pos*flag);    //算出前面确定的位中每个数字出现次数
            for(j=0;j<=9;j++)  
                res[j]+=pre[i]*flag; //算出后面不确定的位中每个数字出现次数
            now+=pos;  
        }
        pos/=10;i--;
    }  
}  
int main()  
{  
    int i;ll a,b,pos=10;  
    pre[1]=1;
    for(i=2;i<=12;i++)  
        pre[i]=pre[i-1]*10+pos,pos*=10;  
        
    cin >> a >> b;  
    Digital_DP(b+1,1);
    Digital_DP(a,-1);
    for(i=0;i<=9;i++) cout << res[i] << " ";
}  

1、对于所有i位中每个数字出现次数的预处理要积累:

pre[i]=pre[i-1]*10^(digit-1)

2、求区间问题,考虑差分,都化为(1,K)的形式

3、数位DP中,特殊处理边界

     数字出现次数,特殊处理前导0

猜你喜欢

转载自www.cnblogs.com/newera/p/9080152.html