LG-P2602 [ZJOI2010]數字計數
阿新 • • 發佈:2018-12-17
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 的情況 ① 這一位小於 x 後面的低位隨便,之前的範圍是 0~pre,共 pre+1 個,但是當 j=0 時,pre 不能取到 0,否則就和之前的狀態重複了。 ② pre 同理,sub 的取值為 0 到 sub,共 sub+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;
}