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

Luogu P2602 [ZJOI2010]數字計數

ret pre 繼續 con 如何 long long 成功 了吧 位數

這算是一道數位DP的入門題了吧雖然對於我來說還是有點煩

經典起手式不講了吧,\(ans(a,b)\to ans(1,b)-ans(1,a-1)\)

我們首先預處理一個東西,用\(f_i\)表示有\(i\)位數字的時候,每個數字有幾個(註意是和)。若不考慮前導零,則所有數字都是等價的,轉移為:

\(f_i=10\cdot f_{i-1}+10^{i-1}\)

這個還是比較好理解的吧,前面一項表示無論這一位放什麽直接從前面拿過來已有的,所以這一位可以放\(0\to9\)十個數,後面一項表示當這一位放上想要的數字時和前面得出的貢獻。

我們發現這樣就很巧妙的避免了重復,因為前後兩次計算剛好會產生多次貢獻。

然後考慮如何根據\(f_i\)

推出答案。假設現在的數為\(ABCD\)

和常規的數位DP一樣,我們優先考慮首位對答案的共獻,由於\(1000,2000,...,A000\)都小於等於\(ABCD\),可以直接加入貢獻,所以我們將答案加上\(A\cdot f_3\)即可。

然後就是剩下的\(A000\to ABCD\)部分了,但是我們發現這個的求解過程就相當於\(BCD\)的貢獻。

所以我們成功將大問題分解,接下來就是繼續算下一位的貢獻了

最後還有一個關於前導零的問題了,我們繼續手玩一波

當首位為\(0\)時,後面的\(10^{i-1}\)位都是不合法的,以此類推,當前兩位為\(0\)時,後面的\(10^{i-2}\)位也不合法。

因此\(0\)的貢獻應該減去\(\sum_{i=0}^{len-1} 10^i\)

(\(len\)表示數字位數)

然後可喜可賀,終於做完了,上CODE吧

#include<cstdio>
#include<cctype>
using namespace std;
const int N=15;
const long long pow[12]={1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000,10000000000,100000000000};
long long a,b,c[10],d[10],f[N];
int bit[N],cnt;
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(long long &x)
{
    x=0; char ch; while (!isdigit(ch=tc()));
    while (x=(x<<3)+(x<<1)+ch-‘0‘,isdigit(ch=tc()));
}
inline void write(long long x)
{
    if (x>9) write(x/10);
    putchar(x%10+‘0‘);
}
inline void init(void)
{
    for (register int i=1;i<=12;++i) f[i]=f[i-1]*10+pow[i-1];
}
inline void resolve(long long x)
{
    cnt=0; while (x) bit[++cnt]=x%10,x/=10;
}
inline void solve(long long x,long long *num)
{
    register int i,j; resolve(x);
    for (i=cnt;i>=1;--i)
    {
        for (j=0;j<bit[i];++j) num[j]+=pow[i-1];
        for (j=0;j<=9;++j) num[j]+=f[i-1]*bit[i];
        long long tot=0;
        for (j=i-1;j>=1;--j) tot=tot*10+bit[j];
        num[bit[i]]+=tot+1; num[0]-=pow[i-1];
    }
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register int i; read(a); read(b); init(); 
    solve(a-1,c); solve(b,d);
    for (i=0;i<=9;++i) write(d[i]-c[i]),putchar(‘ ‘);
    return 0;
}

Luogu P2602 [ZJOI2010]數字計數