BZOJ 1005-明明的烦恼-(prufer序列+高精度)

1005: [HNOI2008]明明的烦恼

Time Limit: 1 Sec   Memory Limit: 162 MB
Submit: 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

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;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值