1. 程式人生 > >#HDU4390#表示式計數(第二類Stirling + 容斥)

#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
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;
}