1. 程式人生 > >【BZOJ 1211】HNOI2004]樹的計數(組合數學+Purfer序列)

【BZOJ 1211】HNOI2004]樹的計數(組合數學+Purfer序列)

1211: [HNOI2004]樹的計數 Time Limit: 10 Sec Memory Limit: 162 MB Submit: 3149 Solved: 1181 [Submit][Status][Discuss] Description

一個有n個結點的樹,設它的結點分別為v1, v2, …, vn,已知第i個結點vi的度數為di,問滿足這樣的條件的不同的樹有多少棵。給定n,d1, d2, …, dn,程式設計需要輸出滿足d(vi)=di的樹的個數。 Input

第一行是一個正整數n,表示樹有n個結點。第二行有n個數,第i個數表示di,即樹的第i個結點的度數。其中1<=n<=150,輸入資料保證滿足條件的樹不超過10^17個。 Output

輸出滿足條件的樹有多少棵。 Sample Input 4

2 1 2 1

Sample Output 2

在寫這道題之前我們先了解一下什麼叫做Purfer序列。 Purfer序列是通過尋找一棵樹最小的葉節點AA,把與這個葉節點相連的非根節點BB加入到陣列中,並刪除A,B(A,B),直到剩下兩個點為止, 這n-2個數就是Purfer序列,每顆樹只有一個Purfer序列。並且每個點最多在Purfer序列種出現(1)(度數-1)次。 因此最後的答案為 (n2)!/i=1n(deed[i]1)(n-2)!/\prod_{i=1}^n(deed[i]-1)

)!/i=1n(deed[i]1)。 在這個過程中,乘法會爆long long,因此我們需要進行質因數分解優化。 把參與運算的每個數質因數分解,除法的時候直接消去即可。 最終在把剩下的數乘起來。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
const int INF = 0x3f3f3f3f;
typedef long long ll;
ll a[maxn];
ll cnt[maxn];
void solve(ll n,ll kk){
	for(ll i=2;i*i<=n;i++){
		while(n%i==0){
			cnt[i]+=kk;
			n/=i;
		}
	}
	cnt[n]+=kk;
	return ;
}
int main(){
	ll n;
	cin>>n;
	ll sum=0;
	for(ll i=1;i<=n;i++){
		cin>>a[i];
		sum+=a[i];
	}
	for(ll i=1;i<=n;i++){
		if(!a[i]&&n>1){
			cout<<0<<endl;
			return 0;
		}
	}
	if(sum!=2*n-2){
		cout<<0<<endl;
		return 0;
	}
	if(n<=2){
		cout<<1<<endl;
		return 0;
	}
	for(ll i=1;i<=n-2;i++){
		solve(i,1);
	}
	ll ans=1;
	for(ll i=1;i<=n;i++){
		for(ll j=2;j<a[i];j++){
			solve(j,-1);
		}
	}
	for(ll i=1;i<=n;i++){
		for(ll j=1;j<=cnt[i];j++){
			ans*=i;
		}
	}
	cout<<ans<<endl;
	return 0;
}