1. 程式人生 > >[BZOJ 1833] 數字計數

[BZOJ 1833] 數字計數

ron 計數 nbsp dig 預處理 接下來 pan 積累 namespace

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

[BZOJ 1833] 數字計數