1. 程式人生 > >LG-P2602 [ZJOI2010]數字計數

LG-P2602 [ZJOI2010]數字計數

P2602 [ZJOI2010]數字計數 題目連結 題目描述 給定兩個正整數a和b,求在[a,b]中的所有整數中,每個數碼(digit)各出現了多少次。

輸入格式: 輸入檔案中僅包含一行兩個整數a、b,含義如上所述。

輸出格式: 輸出檔案中包含一行10個整數,分別表示0-9在[a,b]中出現了多少次。

輸入樣例: 1 99 輸出樣例: 9 20 20 20 20 20 20 20 20 20 說明 30%的資料中,a<=b<=10^6;

100%的資料中,a<=b<=10^12。

題解 算是數位DP吧。(雖然感覺沒用到DP) 這個直接針對每一位來處理就好了。

對於第 i 位為數字 j 的情況,我們可以直接根據 x 推出,而相反,在windy數中的技巧在這裡難以施展。

對於一個數字我們把它拆成三部分來考慮。 1.第 i 位前面的部分,記作pre 2.第 i 位,記作pi 3.第 i 位後面的部分,記作sub 例如 1390,i=2時 的三部分分別為13,9,0;i=4時,三部分分別為0,3,90

第 i 位為某個數字 j 的情況 ① j&lt;pitimes=(pre+1[j=0])10i1j&lt;pi:times=(pre+1-[j=0])*10^{i-1} 這一位小於 x 後面的低位隨便,之前的範圍是 0~pre,共 pre+1 個,但是當 j=0 時,pre 不能取到 0,否則就和之前的狀態重複了。 ② j

=pitimes=(pre[j=0])10i1+sub+1j=pi:times=(pre-[j=0])*10^{i-1}+sub+1 pre 同理,sub 的取值為 0 到 sub,共 sub+1 種情況。 ③ j&gt;pitimes=(pre[j=0])10i1j&gt;pi:times=(pre-[j=0])*10^{i-1}

綜上即可求解。

程式碼

#include<cstdio>
#include<cstring>
#include
<iostream>
#include<algorithm> using namespace std; const int maxn=15; #define LL long long LL ten[maxn],q[maxn],h[maxn],A,B,ans[maxn]; int p[maxn],n; void DPS(LL x,LL d) { n=0; for (LL y=x;y;y/=10) p[++n]=y%10; if (!n) {ans[0]+=d;return;} q[n+1]=0;for (int i=n;i>=1;--i) q[i]=q[i+1]*10+p[i];q[n+1]=1; h[0]=0; for (int i=1;i<=n;++i) h[i]=h[i-1]+p[i]*ten[i-1]; for (int i=1;i<n;++i) for (int j=0;j<10;++j) { LL pre=q[i+1]-(j>=p[i]?1:0),sub=h[i-1]; ans[j]+=(pre+(j>0))*ten[i-1]*d; if (j==p[i]) ans[j]+=(sub+1)*d; } for (int j=1;j<=p[n];++j) if (j==p[n]) ans[j]+=(h[n-1]+1)*d;else ans[j]+=ten[n-1]*d; } int main() { scanf("%lld%lld",&A,&B);*ten=1; for (int i=1;i<=12;++i) ten[i]=ten[i-1]*10; DPS(B,1);DPS(A-1,-1); for (int i=0;i<9;++i) printf("%lld ",ans[i]); printf("%lld\n",ans[9]); return 0; }