【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序列是通过寻找一棵树最小的叶节点 A A ,把与这个叶节点相连的非根节点 B B 加入到数组中,并删除 A , B (A,B) ,直到剩下两个点为止,
这n-2个数就是Purfer序列,每颗树只有一个Purfer序列。并且每个点最多在Purfer序列种出现 ( 1 ) (度数-1) 次。
因此最后的答案为
( n 2 ) ! / i = 1 n ( d e e d [ i ] 1 ) (n-2)!/\prod_{i=1}^n(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;
}

猜你喜欢

转载自blog.csdn.net/duanghaha/article/details/82805599