[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++