1. 程式人生 > >P2518 [HAOI2010]計數

P2518 [HAOI2010]計數

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]計數