[HNOI2008 Tree]

[关键字]:Prüfer编码 Cayley定理

[题目大意]:告诉你N结点的树上部分点的度数,求这样的树一共有多少棵.

//==================================================================================

[分析]:刚刚看到这题时一点思路也没有,又想了一会儿,还是没思路……这题其实和Prüfer编码Cayley定理有关系,Cayley定理:一个n个节点的无根树有nn-2种形态。这个定理可以通过Prüfer编码来证明,见这里。通过Prüfer编码的推广可以得到n各右度限制的节点的无根树的个数,而n各有些有度限制的无根树个数怎么算呢?首先把有度限制的先算出来:

tot!/(d1-1)!/(d2-1)!……/(di-1)!(tot是所有度之和),因为实在n-2个位置中选出tot个所以还有再乘C(n-2,tot),而剩下的没有度限制的节点需要放在n-2-tot个位置,一共有

(n-tot)(n-2-tot)种方案,所以总共的方案数为:(n-tot)(n-2-tot)*C(n-2,tot)*tot!/(d1-1)!/(d2-1)!……/(di-1)!,稍微化简一下计算,除法要用分解质因数乘法要用高精度。

[代码]:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN=10000;

int n,tot,now,c,sum;
int a[MAXN],s[MAXN],p[MAXN];
int ans[MAXN];
bool v[MAXN];

void Add(int x,int c)
{
for (int i=1;i<=sum;++i)
while (x%s[i]==0)
{
p[i]+=c;
x/=s[i];
}
}

void Cheng(int t)
{
int k=0;
for (int i=1;i<=c;++i)
{
ans[i]=ans[i]*t+k;
k=ans[i]/10;
ans[i]=ans[i]%10;
}
while (k)
{
ans[++c]=k%10;
k=k/10;
}
}

void Init()
{
scanf("%d",&n);
for (int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
if (a[i]!=-1) tot+=a[i]-1; else ++now;
}
memset(v,0,sizeof(v));
for (int i=2;i<=n;++i)
{
if (!v[i]) s[++sum]=i;
for (int j=1;(j<=sum && i*s[j]<=n);++j)
{
v[i*s[j]]=1;
if (i%s[j]==0) break;
}
}
}

void Solve()
{
for (int i=n-1-tot;i<=n-2;++i) Add(i,1);
for (int i=1;i<=n;++i)
if (a[i]!=-1)
for (int j=2;j<a[i];++j) Add(j,-1);
for (int i=1;i<=n-2-tot;++i) Add(now,1);
c=1;
ans[1]=1;
for (int i=1;i<=sum;++i)
for (int j=1;j<=p[i];++j)
Cheng(s[i]);
for (int i=c;i>=1;--i)
printf("%d",ans[i]);
printf("\n");
}

int main()
{
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
Init();
Solve();
return 0;
}



你可能感兴趣的:(tree)