#HDU4390#表示式計數(第二類Stirling + 容斥)
[HDU4390]表示式計數
時間限制: 1 Sec 記憶體限制: 128 MB題目描述
給出n個數,b1,b2,b3……bn,構造n個數,a1,a2,……an(ai>1),使得a1*a2*a3……an=b1*b2……bn; 問一共有多少種數列a1,a2,……an滿足上述條件。輸入
包含多組輸入資料 每組資料第一行有1個整數n(1<=n<=20) 每組資料第 二行有n個整數第i個數表示bi.(1<bi<=1000000)且b1*b2*…*bn <10^25)。輸出
對於每組測試資料,輸出有多少種數列滿足情況,結果對1e9+7取餘
樣例輸入
2
3 4
樣例輸出
4
資料規模太大,所以只能考慮將b1*b2*....*bn分解質因數,由唯一分解定理可得,對於該數列的乘積,只有一種分解方式,即每種質因子的個數都是一定的。
對乘積分解質因子得 a[i] 表示第 i 種質因子的個數
問題可以轉化成:
現有i種不同顏色的小球,每種小球有a[i]個,還有N個不同的盒子,求將所有小球裝進N個盒子的方案數,盒子不能為空( i從0開始算 )
數列順序可交換,所以對應的盒子是不相同的
如{1,2} {2,1}是兩個數列
對於第i種小球,可以看做是將a[i]個相同的小球放進N個不同的盒子中,盒子可以為空(稍後解釋)
此時方案數為C[ a[i] + N - 1, N - 1 ],證明如下:(即第二類Stirling的證明
設每個盒子中裝的小球數量為Xj,Xj>=0,則是求此方程的解有多少組
X1+X2+X3+....+Xn = a[i]
X1+X2+X3+....+Xn + N = a[i] + N
此時就變成了a[i]+N個小球放進N個不同盒子中,每個盒子至少裝一個
使用隔板法:
在這些小球中共有a[i]+N-1個空位可以放置隔板,放置N - 1個隔板即將它們分成N組(N盒)
從a[i]+N-1個位置中選擇N - 1個位置的方案數為C[ a[i]+N-1 , N-1 ]
對於一種顏色小球的一種方案,其他小球的每一種方案都可以與之相適應,所以此時i種小球的總方案數為∑C[ a[i]+ N-1 , N-1 ] (0<=i<=cnt)
但是這裡有個很明顯的bug,在第i種小球的方案中,有很多是有空盒子的,這本來是必須的(因為新數列中的數並不是每個數都要含有乘積中的每個質因子)
當i種小球放在一起,就會出現空盒子(一種小球都沒有),這樣的情況還有很多,空盒子為0~N-1個
所以要去重複
有容斥原理:A1類元素A2類元素A3類元素....An類元素個數總和=∑Ai (1<=i<=N) - ∑既是Ai類又是Aj類元素個數總和(1<=i<=j<=N) + ∑既是Ai類又是Aj類又是Ak類元素個數總和(1<=i<=j<=k<=N) - .... + [(-1)^(N-1)]*(既是A1又是A2又是A3....又是An類元素個數總和)
在此題中,Ai類指∑第 j 種顏色的小球,空了i個盒子的方案數(即 將a[j]個相同小球放進N-i-1個盒子中的方案數) 1<=i<N , 0<=j<=cnt
如:
A1類指∑第j種顏色小球空了1個盒子,即放置在N-1個盒子中C[a[j]+N-1, N-1-1],1<=i<N , 0<=j<=cnt
A2類指∑第j種顏色小球空了2個盒子,即放置在N-2個盒子中C[a[j]+N-1, N-1-2],1<=i<N , 0<=j<=cnt
....
Acnt類指∑第j種顏色小球空了n-1個盒子,即放置在1個盒子中C[a[j]+N-1, 0],1<=i<N , 0<=j<=cnt
最後的答案即是:總方案數-有空盒的方案數
Code:
Status | Accepted |
---|---|
Memory | 3680kB |
Length | 1430 |
Lang | G++ |
Submitted | 2017-07-08 20:11:28 |
Shared | |
RemoteRunId | 21030727 |
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<vector>
using namespace std;
typedef long long LL;
const int MOD = 1e9 + 7;
int N;
LL Ans;
int a[1005];
LL C[505][505];
vector<int>P;
void pre_work(){
C[0][0] = 1;
for(int i = 1; i <= 500; ++ i){
C[i][0] = C[i][i] = 1;
for(int j = 1; j < i; ++ j)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD;
}
}
void solve(int cnt){//將格子是否放置質因數進行容斥
Ans = 1LL;//因為直接根據每種質因子分進N個格子的方案中,直接相乘會造成有的格子裡一個質因子也沒有,而題目要求ai > 1,所以不能有格子被空著不放
for(int i = 0; i <= cnt; ++ i)
Ans = (Ans * C[a[i] + N - 1][N - 1]) % MOD;//每種質因子被無限制條件地分進N個格子中的方案數(可以放,可以不放)
for(int i = 1; i < N; ++ i){//N個格子的加減容斥
LL tmp = C[N][i];//N個格子中有i個格子空著不放的方案數
for(int j = 0; j <= cnt; ++ j)//第j種質因子放入剩下的N - i個格子中的方案數
tmp = (tmp * C[N + a[j] - i - 1][N - i - 1]) % MOD;
if(i & 1) Ans = ((Ans - tmp) % MOD + MOD) % MOD;
else Ans = (Ans + tmp) % MOD;
}
}
int main(){
pre_work();
int x;
while(~scanf("%d", &N)){
for(int i = 1; i <= N; ++ i){
scanf("%d", &x);
for(int j = 2; j * j <= x; ++ j)
while(x % j == 0)
x /= j, P.push_back(j);
if(x > 1) P.push_back(x);
}
sort(P.begin(), P.end());
int cnt = 0, len = P.size();
a[0] = 1;
for(int i = 1; i < len; ++ i)
if(P[i] == P[i - 1]) ++ a[cnt];
else a[++ cnt] = 1;
solve(cnt);//一共有cnt種質因子
printf("%lld\n", Ans);
memset(a, 0, sizeof(a));
P.clear();
}
return 0;
}