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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值