您的位置:首页 > 其它

[HNOI2008]明明的烦恼

2017-12-24 18:12 169 查看

Description

  自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在

任意两点间连线,可产生多少棵度数满足要求的树?

Input

  第一行为N(0 < N < = 1000),

接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1

Output

  一个整数,表示不同的满足要求的树的个数,无解输出0

Sample Input

3

1

-1

-1

Sample Output

2

HINT

  两棵树分别为1-2-3;1-3-2

[b]该题运用到了树的prufer编码的性质:[/b]
[b] (1)树的prufer编码的实现[/b]
[b] 不断 删除树中度数为1的最小序号的点,并输出与其相连的节点的序号 直至树中只有两个节点[/b]
[b] (2)通过观察我们可以发现[/b]
[b] 任意一棵n节点的树都可唯一的用长度为n-2的prufer编码表示[/b]
[b] 度数为m的节点的序号在prufer编码中出现的次数为m-1[/b]
[b] (3)怎样将prufer编码还原为一棵树??[/b]
[b] 从prufer编码的最前端开始扫描节点,设该节点序号为 u ,寻找不在prufer编码的最小序号且没有被标记的节点 v ,连接 u,v,并标记v,将u从prufer编码中删除。扫描下一节点。[/b]
[b]该题需要将树转化为prufer编码[/b]
[b]因为一个点度为di,那么在prufer序列中出现di-1次[/b]
[b]所以对于已知的度,sum=∑di-1(已知),cnt为有多少已知点[/b]
[b]那么从序列中选出sum为方案C(sum,n-2)[/b]
[b]对于已知di,产生的方案数为${{(n-2)!} \over {\prod (d_i - 1)}}$[/b]
[b]对于无限制的点,可以这样考虑,剩下的n-2-sum为每一位选择都有n-cnt种[/b]
[b]所以方案为(n-cnt)n-2-sum[/b]
[b]把三者乘起来[/b]

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
struct Big_Num
{
int a[5005],len;
Big_Num()
{}
Big_Num &operator *=(const int &b)
{int i;
for (i=1;i<=len;i++)
a[i]*=b;
for (i=1;i<=len;i++)
a[i+1]+=a[i]/10,a[i]%=10;
int loc=len+1;
while (a[loc])
{
a[loc+1]+=a[loc]/10;
a[loc]%=10;
loc++;
}
len=loc-1;
}
void print()
{int i;
for (i=len;i>=1;i--) printf("%d",a[i]);
cout<<endl;
}
}ans;
int d[1001],du[2001],pri[2001],pre[2001],tot,n,cnt,sum,flag;
bool vis[2001];
int main()
{int i,j;
freopen("tree1.in","r",stdin);
freopen("1005.out","w",stdout);
cin>>n;
flag=0;
for (i=1;i<=n;i++)
{
scanf("%d",&d[i]);
if (d[i]!=-1) cnt++,sum+=d[i]-1;
if (d[i]==0||d[i]==n) flag=1;
}
if (n==1)
{
cout<<1;
return 0;
}
if (n==2)
{
if ((d[1]==0||d[1]>1)||(d[2]==0||d[2]>1))
cout<<0;
else cout<<1;
return 0;
}
if (sum>n-2)
{
cout<<0;
return 0;
}
if (flag)
{
cout<<0;
return 0;
}
for (i=2;i<=n-2;i++)
du[i]++;
for (i=2;i<=n-2-sum;i++)
du[i]--;
for (i=1;i<=n;i++)
if (d[i]!=-1)
{
for (j=2;j<=d[i]-1;j++)
du[j]--;
}
for (i=1;i<=n-2-sum;i++)
du[n-cnt]++;

for (i=2;i<=2000;i++)
{
if (vis[i]==0)
{
pri[++tot]=i;
pre[i]=i;
}
for (j=1;j<=tot;j++)
{
if (pri[j]*i>2000) break;
vis[i*pri[j]]=1;
pre[i*pri[j]]=pri[j];
if (i%pri[j]==0) break;
}
}
for (i=2000;i>=2;i--)
if (pre[i]!=i)
{
du[pre[i]]+=du[i];
du[i/pre[i]]+=du[i];
du[i]=0;
}
ans.a[1]=1;ans.len=1;
for (i=2;i<=2000;i++)
if (du[i]>0)
{
for (j=1;j<=du[i];j++)
ans*=i;
}
ans.print();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: