【BZOJ1005/1211】[HNOI2008]明明的烦恼/[HNOI2004]树的计数 Prufer序列+高精度

【BZOJ1005】[HNOI2008]明明的烦恼

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序列中出现的次数=这个点的度数-1,所以我们令m为已经确定度数的点的数量,$tot=\sum D[i]-1$,那么其他位置可以随便选,方案数为$(n-2-tot)^{n-m}$。然后这些已经确定的位置可以随便排列,方案数为$C_{n-2}^{tot} \times C_{tot}^{d1} \times C_{tot-d1}^{d2} \times ...$。

但显然结果是爆longlong的,不过可以确定答案一定是个整数。为了避免高精度除法,我们可以将分子和分母都拆成质数的几次方形式,然后分子分母做减法,最后用高精度将这些质数乘起来就行了。

1211那题需要特判:tot!=n-2输出0;n>1且di=0输出0;di>=n输出0。

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int maxn=1010;
int n,m,tot,num;
int pri[maxn],np[maxn],lp[maxn],s[maxn],d[maxn],B[maxn];
struct Cbig
{
	int a[10010],len;
	Cbig() {memset(a,0,sizeof(a)),len=1;}
	int & operator [] (int b) {return a[b];}
	void operator *= (const int &b)
	{
		for(int i=1;i<=len;i++)	a[i]*=b;
		for(int i=1;i<=len;i++)	a[i+1]+=a[i]/1000,a[i]%=1000;
		while(a[len+1])	len++;
		while(len&&!a[len])	len--;
	}
}ans;
inline void add(int x,int v)
{
	while(x!=1)	s[lp[x]]+=v,x/=pri[lp[x]];
}
void init()
{
	int i,j;
	for(i=2;i<=n;i++)
	{
		if(!np[i])	pri[++num]=i,lp[i]=num;
		for(j=1;j<=num&&i*pri[j]<=n;j++)
		{
			np[i*pri[j]]=1,lp[i*pri[j]]=j;
			if(i%pri[j]==0)	break;
		}
	}
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
int main()
{
	n=rd(),tot=0,init();
	int i;
	for(i=1;i<=n;i++)
	{
		d[i]=rd();
		if(d[i]==0)
		{
			printf("0");
			return 0;
		}
		if(d[i]>0)	d[i]--,m++,tot+=d[i];
	}
	if(tot>n-2)
	{
		printf("0");
		return 0;
	}
	ans.a[1]=ans.len=1;
	for(i=1;i<=n-2-tot;i++)	ans*=(n-m);
	B[n-2]++,B[n-2-tot]--;
	for(i=1;i<=n;i++)	if(d[i]>=0)	B[d[i]]--,tot-=d[i];
	for(i=n-2;i>=1;i--)	B[i]+=B[i+1],add(i,B[i]);
	for(i=1;i<=n-2;i++)	while(s[i]--)	ans*=pri[i];
	printf("%d",ans.a[ans.len]);
	for(i=ans.len-1;i>=1;i--)	printf("%03d",ans.a[i]);
	return 0;
}//5 1 1 -1 -1 -1
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int maxn=1010;
int n,tot,num;
int pri[maxn],np[maxn],lp[maxn],s[maxn],d[maxn],B[maxn];
struct Cbig
{
	int a[10010],len;
	Cbig() {memset(a,0,sizeof(a)),len=1;}
	int & operator [] (int b) {return a[b];}
	void operator *= (const int &b)
	{
		for(int i=1;i<=len;i++)	a[i]*=b;
		for(int i=1;i<=len;i++)	a[i+1]+=a[i]/1000,a[i]%=1000;
		while(a[len+1])	len++;
		while(len&&!a[len])	len--;
	}
}ans;
inline void add(int x,int v)
{
	while(x!=1)	s[lp[x]]+=v,x/=pri[lp[x]];
}
void init()
{
	int i,j;
	for(i=2;i<=n;i++)
	{
		if(!np[i])	pri[++num]=i,lp[i]=num;
		for(j=1;j<=num&&i*pri[j]<=n;j++)
		{
			np[i*pri[j]]=1,lp[i*pri[j]]=j;
			if(i%pri[j]==0)	break;
		}
	}
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
int main()
{
	n=rd(),tot=0,init();
	int i;
	for(i=1;i<=n;i++)
	{
		d[i]=rd();
		if((n!=1&&d[i]==0)||d[i]>=n)
		{
			printf("0");
			return 0;
		}
		d[i]--,tot+=d[i];
	}
	if(tot!=n-2)
	{
		printf("0");
		return 0;
	}
	ans.a[1]=ans.len=1;
	B[n-2]++,B[n-2-tot]--;
	for(i=1;i<=n;i++)	if(d[i]>=0)	B[d[i]]--,tot-=d[i];
	for(i=n-2;i>=1;i--)	B[i]+=B[i+1],add(i,B[i]);
	for(i=1;i<=n-2;i++)	while(s[i]--)	ans*=pri[i];
	printf("%d",ans.a[ans.len]);
	for(i=ans.len-1;i>=1;i--)	printf("%03d",ans.a[i]);
	return 0;
}//5 1 1 -1 -1 -1
时间: 2024-11-05 18:28:12

【BZOJ1005/1211】[HNOI2008]明明的烦恼/[HNOI2004]树的计数 Prufer序列+高精度的相关文章

BZOJ 1211 HNOI2004 树的计数 Prufer序列

题目大意:给定一棵树中所有点的度数,求有多少种可能的树 Prufer序列,具体参考[HNOI2008]明明的烦恼 直接乘会爆long long,所以先把每个数分解质因数,把质因数的次数相加相减,然后再乘起来 注意此题无解需要输出0 当n!=1&&d[i]==0时 输出0 当Σ(d[i]-1)!=n-2时输出0 写代码各种脑残--居然直接算了n-2没用阶乘-- #include<cstdio> #include<cstring> #include<iostrea

【bzoj1005】[HNOI2008]明明的烦恼 Prufer序列+高精度

题目描述 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? 输入 第一行为N(0 < N < = 1000),接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1 输出 一个整数,表示不同的满足要求的树的个数,无解输出0 样例输入 3 1 -1 -1 样例输出 2 题解 Prufer序列+高精度 Prufer序列:由一棵 $n$ 个点的树唯一产生的一个 $n-2$ 个数的序列. 生成方法:找到这棵树编号最小的叶子节点,将其

BZOJ 1211: [HNOI2004]树的计数 purfer序列

1211: [HNOI2004]树的计数 Description 一个有n个结点的树,设它的结点分别为v1, v2, …, vn,已知第i个结点vi的度数为di,问满足这样的条件的不同的树有多少棵.给定n,d1, d2, …, dn,编程需要输出满足d(vi)=di的树的个数. Input 第一行是一个正整数n,表示树有n个结点.第二行有n个数,第i个数表示di,即树的第i个结点的度数.其中1<=n<=150,输入数据保证满足条件的树不超过10^17个. Output 输出满足条件的树有多少棵

[HNOI2008]明明的烦恼

1005: [HNOI2008]明明的烦恼 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 5090  Solved: 1986[Submit][Status][Discuss] Description 自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? Input 第一行为N(0 < N < = 1000),接下来N行,第i+1行给出第i个节点的

BZOJ 1005: [HNOI2008]明明的烦恼 Purfer序列 大数

1005: [HNOI2008]明明的烦恼 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=1005 Description 自从明明学了树的结构,就对奇怪的树产生了兴趣...... 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? Input 第一行为N(0 < N < = 1000),接下来N行,第i+1行

bzoj 1005: [HNOI2008]明明的烦恼 prufer编号&amp;&amp;生成树计数

1005: [HNOI2008]明明的烦恼 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 2248  Solved: 898[Submit][Status] Description 自从明明学了树的结构,就对奇怪的树产生了兴趣...... 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? Input 第一行为N(0 < N < = 1000),接下来N行,第i+1行给出第i个节点的度数Di,如果对度

bzoj 1005: [HNOI2008]明明的烦恼(组合数学 purfer sequence)

1005: [HNOI2008]明明的烦恼 Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 3945  Solved: 1563 [Submit][Status][Discuss] Description 自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在 任意两点间连线,可产生多少棵度数满足要求的树? Input 第一行为N(0 < N < = 1000), 接下来N行,第i+1行给出第i

BZOJ 1005: [HNOI2008]明明的烦恼 Purfer数列

Purfer数列: http://www.cnblogs.com/zhj5chengfeng/p/3278557.html 题目大意 自从明明学了树的结构,就对奇怪的树产生了兴趣...... 给出标号为 1 到 N 的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? Input 第一行为 N(0<N<=1000),接下来 N 行,第 i+1 行给出第 i 个节点的度数 Di,如果对度数不要求,则输入 -1 Output 一个整数,表示不同的满足要求的树的个数,无解

BZOJ1005--[HNOI2008]明明的烦恼(树的prufer编码)

1005: [HNOI2008]明明的烦恼 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 5768  Solved: 2253[Submit][Status][Discuss] Description 自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在 任意两点间连线,可产生多少棵度数满足要求的树? Input 第一行为N(0 < N < = 1000), 接下来N行,第i+1行给出第i个节