1005: [HNOI2008]明明的烦恼
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 5200 Solved: 2035
[ Submit][ Status][ Discuss]
Description
自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在
任意两点间连线,可产生多少棵度数满足要求的树?
Input
第一行为N(0 < N < = 1000),
接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1
Output
一个整数,表示不同的满足要求的树的个数,无解输出0
Sample Input
3
1
-1
-1
1
-1
-1
Sample Output
2
HINT
两棵树分别为1-2-3;1-3-2
写这道题前需要先科普一下:prufer序列
在了解之后,如何用这个序列来解这道题呢
该题运用到了树的prufer编码的性质:
1:任意一点的度为d,那么这个数一定会在这个序列中存在d-1个
2:序列和树一一对应
该题需要将树转化为prufer编码:
n为树的节点数,d[ ]为各节点的度数,m为无限制度数的节点数。
则
所以要求在n-2大小的数组中插入tot各序号,共有种插法;
在tot各序号排列中,插第一个节点的方法有种插法;
插第二个节点的方法有种插法;
………
另外还有m各节点无度数限制,所以它们可任意排列在剩余的n-2-tot的空间中,排列方法总数为;
根据乘法原理:
#include<map>
#include<stack>
#include<queue>
#include<vector>
#include<math.h>
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn 1005
ll a[maxn]={1,1},b[maxn],f[maxn],n,cost[maxn],cnt,ans[maxn];
void work(ll x,ll y)
{
if(x==0)
return;
for(int i=1;i<=cnt;i++)
{
while(x%b[i]==0)
{
x/=b[i];
cost[i]+=y;
}
}
}
int main(void)
{
ll i,j,mark=0,num=0,sum=0,len;
for(i=2;i<=1000;i++)
{
if(a[i])
continue;
b[++cnt]=i;
for(j=i*i;j<=1000;j+=i)
a[j]=1;
}
scanf("%lld",&n);
for(i=1;i<=n;i++)
{
scanf("%lld",&f[i]);
if(f[i]==0 || f[i]>=n)
mark=1;
if(f[i]!=-1)
{
num++;
sum+=f[i]-1;
for(j=2;j<=f[i]-1;j++)
work(j,-1);//部分分母中需要约掉的质因数
}
}
if(mark || n-2-sum<0)
printf("0\n");
else if(n==1)
{
if(f[1]>=1)
printf("0\n");
else
printf("1\n");
}
else if(n==2)
{
if(f[1]>1 || f[2]>1 || f[1]==0 || f[2]==0)
printf("0\n");
else
printf("1\n");
}
else
{
for(i=n-2-sum+1;i<=n-2;i++)
work(i,1);//分子中需要约掉的质因数
work(n-num,n-2-sum);//因为需要开n-2-sum次方,故每个质因数要在原有的基础上多用
//n-2-sum倍
ans[1]=1;len=1;
for(i=1;i<=cnt;i++)
{
while(cost[i])
{
for(j=1;j<=len;j++)
ans[j]*=b[i];
for(j=1;j<len;j++)
{
ans[j+1]+=ans[j]/10;
ans[j]=ans[j]%10;
}
while(ans[len]>=10)
{
ans[len+1]=ans[len]/10;
ans[len]%=10;
len++;
}
cost[i]--;
}
}
for(i=len;i>0;i--)
printf("%lld",ans[i]);
printf("\n");
}
return 0;
}