题目大意:给定一棵n个节点的树的节点的度数,其中一些度数无限制,求可以生成多少种树
Prufer序列
把一棵树进行以下操作:
1.找到编号最小的叶节点,删除这个节点,然后与这个叶节点相连的点计入序列
2.反复进行1,直到这棵树只剩下两个节点时,退出
比如说这个图(来自度受百科)
最小叶节点为2,删除2,将3计入序列
最小叶节点为4,删除4,将5计入序列
最小叶节点为5,删除5,将1计入序列
最小叶节点为1,删除1,将3计入序列
图中只剩下两个节点,退出
于是得到这棵树的Prufer序列为{3,5,1,3}
这样可以得到一个长度为n-2的序列。很容易证明,树和Prufer序列是一一对应的
Prufer序列显然满足一个性质:一个点若度数为d,则一定在Prufer序列中出现了d-1次
于是这就变成了一个排列组合的问题了
令每个已知度数的节点的度数为di,有n个节点,m个节点未知度数,left=(n-2)-(d1-1)-(d2-1)-...-(dk-1)
已知度数的节点可能的组合方式如下
(n-2)!/(d1-1)!/(d2-1)!/.../(dk-1)!/left!
剩余left个位置由未知度数的节点随意填补,方案数为m^left
于是最后有
ans=(n-2)!/(d1-1)!/(d2-1)!/.../(dk-1)!/left! * m^left
答案很显然有高精度,为了避免高精度除法我们可以对每个阶乘暴力分解质因数,对指数进行加减操作即可
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 1100
using namespace std;
typedef long long ll;
struct abcd{
ll xx[400];
int cnt;
abcd(int x=0)
{
memset(xx,0,sizeof xx);
xx[1]=x;
cnt=1;
}
ll& operator [] (int x)
{
return xx[x];
}
}ans(1);
abcd operator *= (abcd &x,abcd &y)
{
int i,j;
abcd z;
for(i=1;i<=x.cnt;i++)
for(j=1;j<=y.cnt;j++)
z[i+j-1]+=x[i]*y[j],z[i+j]+=z[i+j-1]/100000000,z[i+j-1]%=100000000;
z.cnt=x.cnt+y.cnt;
if(!z[z.cnt])
--z.cnt;
x=z;
}
ostream& operator << (ostream& os,abcd &x)
{
int i;
printf("%lld",x[x.cnt]);
for(i=x.cnt-1;i;i--)
printf("%08lld",x[i]);
return os;
}
int n,m,remain,cnt[M],stack[M],top;
void Decomposition(int x,int y)
{
int i;
for(i=2;i*i<=x;i++)
while(x%i==0)
cnt[i]+=y,x/=i;
if(x^1)
cnt[x]+=y;
}
void Quick_Power(int i,int y)
{
abcd x(i);
while(y)
{
if(y&1)ans*=x;
x*=x;
y>>=1;
}
}
int main()
{
int i,x;
cin>>n;remain=n-2;
for(i=1;i<=n;i++)
{
scanf("%d",&x);
if(x==-1)
++m;
else if(x>1)
stack[++top]=x-1,remain-=x-1;
}
for(i=2;i<=n-2;i++)
Decomposition(i,1);
while(top)
{
for(i=2;i<=stack[top];i++)
Decomposition(i,-1);
stack[top--]=0;
}
for(i=2;i<=remain;i++)
Decomposition(i,-1);
Decomposition(m,remain);
for(i=1;i<=n;i++)
if(cnt[i])
Quick_Power(i,cnt[i]);
cout<<ans<<endl;
}