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

LG-P2518 [HAOI2010]計數

P2518 [HAOI2010]計數
題目連結
題目描述
你有一組非零數字(不一定唯一),你可以在其中插入任意個0,這樣就可以產生無限個數。比如說給定{1,2},那麼可以生成數字12,21,102,120,201,210,1002,1020,等等。

現在給定一個數,問在這個數之前有多少個數。(注意這個數不會有前導0).

輸入格式:
只有1行,為1個整數n.

輸出格式:
只有整數,表示N之前出現的數的個數。

輸入樣例:
1020
輸出樣例:
7
說明
n的長度不超過50,答案不超過2^63-1.

題解
首先想到DFS,結果看看資料範圍瞬間就萎了。

如果我們確定高位小於 N,那麼剩餘的位數,就用剩下的數進行排列就好了。

從最高位向低位列舉相等的位,然後找一個小於當前位的數字放在當前位,剩下的排列即可。

程式碼

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=55;
#define LL long long
LL c[maxn][maxn],ans=0;
int a[maxn],h[15],n;
char s[maxn];
LL C(int n,int m)
{
	if (n==m||
!m) return 1; if (n<m) return 0; if (m==1) return n; if (c[n][m]) return c[n][m]; return c[n][m]=C(n-1,m)+C(n-1,m-1); } LL SUM() { LL ret=1;int m=n; for (int i=0;i<10;++i) ret*=C(m,h[i]),m-=h[i]; return ret; } int main() { // freopen("st.in","r",stdin);freopen("st.out","w",stdout); scanf("%s"
,s);n=strlen(s); for (int i=0;i<n;++i) ++h[a[i]=s[i]-'0']; for (int i=0,R=n;i<R;++i) { --n; for (int j=a[i]-1;j>=0;--j) { if (!h[j]) continue; --h[j]; ans+=SUM(); ++h[j]; } --h[a[i]]; } printf("%lld\n",ans); return 0; }