P2518 [HAOI2010]計數
阿新 • • 發佈:2018-09-20
namespace line strlen tex clu 超過 輸入輸出 names ext
題目描述
你有一組非零數字(不一定唯一),你可以在其中插入任意個0,這樣就可以產生無限個數。比如說給定{1,2},那麽可以生成數字12,21,102,120,201,210,1002,1020,等等。
現在給定一個數,問在這個數之前有多少個數。(註意這個數不會有前導0).
輸入輸出格式
輸入格式:只有1行,為1個整數n.
輸出格式:只有整數,表示N之前出現的數的個數。
輸入輸出樣例
輸入樣例#1:1020
輸出樣例#1:
7
說明
n的長度不超過50,答案不超過2^63-1.
Solution:
本題組合數學(感覺不像數位dp啊)。
題意就是給你一個50位的數,求用各數位上的數能組成多少小於它的數。
我們把給定的數當作n個數字的一個排列(不足n位的可以理解為含有前導0),那麽題意轉化為字典序小於當前排列的個數有多少個。
於是統計下各個數碼的出現次數,然後就能一位一位的計算情況了。考慮到了第$i$位小於某一排列的情況,直接枚舉第$i$位可填的數字,那麽後面的$n-i$個數位可以隨便填數,設剩下的數碼個數依次為$a_0,a_1,a_2…a_9$,則填$0$有$C(n-i,a_0)$種方案,再填$1$有$C(n-i-a_0,a_1)$種方案…不難得到當前的總方案為$C(n-i,a_0)*C(n-i-a_0,a_1)…*C(n-i-a_0-a_1-…a_8,a_9)$。然後累加每位填數情況的方案數就好了。
代碼:
/*Code by 520 -- 9.18*/ #include<bits/stdc++.h> #define il inline #define ll long long #define RE register #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++) #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--) using namespace std; const int N=55; int n,a[N],cnt,tot[N];char s[N]; ll ans,c[N][N]; il ll calc(){ ll ans=1; int m=cnt; For(i,0,9) if(tot[i]) ans*=c[m][tot[i]],m-=tot[i]; return ans; } int main(){ For(i,0,50) c[i][0]=1; For(i,1,50) For(j,1,i) c[i][j]=c[i-1][j]+c[i-1][j-1]; scanf("%s",s+1),n=cnt=strlen(s+1); For(i,1,cnt) a[i]=(s[i]^48),tot[a[i]]++; For(i,1,n) { cnt--; For(j,0,a[i]-1) if(tot[j]) tot[j]--,ans+=calc(),tot[j]++; tot[a[i]]--; } cout<<ans; return 0; }
P2518 [HAOI2010]計數