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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值