[BZOJ1211]:[HNOI2004]树的计数(prufer序列)

题目传送门


题目描述

一个有n个结点的树,设它的结点分别为$v_1,v_2,…,v_n$,已知第i个结点$v_i$的度数为$d_i$,问满足这样的条件的不同的树有多少棵。给定n,$d_1,d_2,…,d_n$,编程需要输出满足$d_{v_i} = d_i$的树的个数。


输入格式

第一行是一个正整数n,表示树有n个结点。第二行有n个数,第i个数表示$d_i$,即树的第i个结点的度数。


输出格式

输出满足条件的树有多少棵。


样例

样例输入

4
2 1 2 1

样例输出

2


数据范围与提示

$1 \leqslant n \leqslant 150$。

输入数据保证满足条件的树不超过${10}^{17}$个。


题解

显然是一道prufer序列的板子题,确定n个点度数分别是为$d_1,d_2,...$时无根树个数:$\frac{(n-2)!}{(d_1-1)! \times (d_2-1)! \times ...}$

但是需要注意两个特判:

  1.要满足是一棵树则$\sum \limits_{i=1}^{n}d_i=2\times n-2$。

  2.当$n=1$时显然不方便处理,则直接输出1即可。

直接输出“0”你会惊喜的发现,你有15分!

至于实现过程,可以使用高精暴力求,当然也可以使用分解质因数,显然后者更好实现。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int n;
int d[151];
int pre[400],pri[400];
long long wzc[400];
long long ans=1LL;
void pre_work()//预处理质因数
{
	for(int i=2;i<=2*n;i++)
	{
		if(!pri[i])
		{
			pri[i]=i;
			pre[++pre[0]]=i;
		}
		for(int j=1;j<=pre[0];j++)
		{
			if(pre[j]>pri[i]||i*pre[j]>n)break;
			pri[i*pre[j]]=pre[j];
		}
	}
}
long long qsm(long long x,long long y)
{
	long long rec=1;
	while(y)
	{
		if(y&1)rec*=x;
		x*=x;
		y>>=1;
	}
	return rec;
}
int main()
{
	scanf("%d",&n);
	pre_work();
	int sum=0;
	bool flag=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&d[i]);
		if(!d[i])flag=1;
		sum+=d[i];
	}
	if(n==1&&!d[1]){puts("1");return 0;}
	if(sum!=2*n-2||flag){puts("0");return 0;}//两个特判
	for(int i=1;i<=n-2;i++)
	{
		int flag=i;
		while(flag>1)
		{
			wzc[pri[flag]]++;
			flag/=pri[flag];
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<d[i];j++)
		{
			int flag=j;
			while(flag>1)
			{
				wzc[pri[flag]]--;
				flag/=pri[flag];
			}
		}
	}
	for(int i=1;i<=n;i++)
		if(wzc[i])ans=ans*qsm(i,wzc[i]);//计算答案
	printf("%lld",ans);
	return 0;
}

rp++

posted @ 2019-07-21 18:44  HEOI-动动  阅读(141)  评论(0编辑  收藏  举报