【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$ 个数的序列。

生成方法:找到这棵树编号最小的叶子节点,将其相邻点加入到序列中,删掉这个点。重复这个过程直到树中只剩下两个点,此时得到的序列即为该树的Prufer序列。

性质:任何一个长度为 $n-2$ ,每个数均在 $1\sum n$ 之间的序列均为一个合法的Prufer序列,对应且只对应着一棵 $n$ 个点的树。

性质:在原树中度数为 $d$ 的点,在Prufer序列中出现了 $d-1$ 次。

根据这两个性质可以考虑本题。给出了每个点的度数限制,即给出了每个点在Prufer序列中出现的次数。对于没给限制的,可以随意选择。

相当于先在 $n-2$ 个数中选出一部分作为没有限制的,剩下的是有限制的。

对于没有限制的,答案就是 $没限制的位置个数^没限制的点的个数$ 。

对于有限制的,使用组合数学的一个公式:长度为 $\sum a_i$ 的序列,第 $i$ 个数出现了 $a_i$ 次的序列数为 $\frac{(\sum a_i)!}{\prod(a_i!)}$ 。

本题不取模,为避免高精度除法,将阶乘分解质因数来处理。

注意特判无解的情况。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define mod 100000000
using namespace std;
typedef long long ll;
struct data
{
	int len;
	ll v[400];
	ll &operator[](int a) {return v[a];}
	data operator+(data &a)
	{
		data ans;
		memset(ans.v , 0 , sizeof(ans.v));
		int i;
		for(i = 0 ; i < len || i < a.len || ans[i] ; i ++ )
			ans[i] += v[i] + a[i] , ans[i + 1] = ans[i] / mod , ans[i] %= mod;
		ans.len = i;
		return ans;
	}
	data operator*(int a)
	{
		data ans;
		memset(ans.v , 0 , sizeof(ans.v));
		int i;
		for(i = 0 ; i < len || ans[i] ; i ++ )
			ans[i] += v[i] * a , ans[i + 1] = ans[i] / mod , ans[i] %= mod;
		ans.len = i;
		return ans;
	}
	void write()
	{
		int i;
		printf("%lld" , v[len - 1]);
		for(i = len - 2 ; ~i ; i -- ) printf("%08lld" , v[i]);
		puts("");
	}
}ans;
int a[1010] , cnt[1010] , prime[1010] , tot , np[1010];
void init()
{
	int i , j;
	for(i = 2 ; i <= 1000 ; i ++ )
	{
		if(!np[i]) prime[++tot] = i;
		for(j = 1 ; j <= tot && i * prime[j] <= 1000 ; j ++ )
		{
			np[i * prime[j]] = 1;
			if(i % prime[j] == 0) break;
		}
	}
}
void solve(int x , int a)
{
	int i , j;
	for(i = 1 ; i <= tot ; i ++ )
		for(j = x / prime[i] ; j ; j /= prime[i])
			cnt[i] += a * j;
}
int main()
{
	init();
	int n , i , c1 = 0 , c2 = 0;
	scanf("%d" , &n);
	for(i = 1 ; i <= n ; i ++ )
	{
		scanf("%d" , &a[i]);
		if(a[i] > 0) c1 += a[i] - 1;
		else if(a[i] == -1) c2 ++ ;
		else
		{
			puts("0");
			return 0;
		}
	}
	if(c1 > n - 2)
	{
		puts("0");
		return 0;
	}
	solve(n - 2 , 1) , solve(n - 2 - c1 , -1);
	for(i = 1 ; i <= n ; i ++ )
		if(a[i] > 0)
			solve(a[i] - 1 , -1);
	ans[0] = ans.len = 1;
	for(i = 1 ; i <= tot ; i ++ )
		while(cnt[i] -- )
			ans = ans * prime[i];
	for(i = 1 ; i <= n - 2 - c1 ; i ++ ) ans = ans * c2;
	ans.write();
	return 0;
}

原文地址:https://www.cnblogs.com/GXZlegend/p/8135459.html

时间: 2024-10-13 13:26:51

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

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

有一种东西叫树的prufer序列,一个树的与一个prufer序列是一一对应的关系. 设有m个度数确定的点,这些点的度为dee[i],那么每个点在prufer序列中出现了dee[i]-1次. 由排列组合可以推出公式.需要用高精度. #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 1100 using namespace std; typede

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序列 大数

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 明明的烦恼 Prufer序列+组合数学+高精度

题目大意:给定一棵n个节点的树的节点的度数,其中一些度数无限制,求可以生成多少种树 Prufer序列 把一棵树进行以下操作: 1.找到编号最小的叶节点,删除这个节点,然后与这个叶节点相连的点计入序列 2.反复进行1,直到这棵树只剩下两个节点时,退出 比如说这个图(来自度受百科) 最小叶节点为2,删除2,将3计入序列 最小叶节点为4,删除4,将5计入序列 最小叶节点为5,删除5,将1计入序列 最小叶节点为1,删除1,将3计入序列 图中只剩下两个节点,退出 于是得到这棵树的Prufer序列为{3,5

[bzoj1005][HNOI2008][明明的烦恼] (高精度+prufer定理)

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-

【prufer编码+组合数学】BZOJ1005 [HNOI2008]明明的烦恼

Description 自从明明学了树的结构,就对奇怪的树产生了兴趣...... 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? Solution 这道题就是树的计数加强版,多了不要求的情况. 对于已限制的情况,就是C(n-2,t)*可重复元素的公式,考虑其他不限制的元素,再*(n-t)^(n-2-sum),t为已限制点个数,sum为已限制度数. 大概就是这个意思,计算要用分解质因数+高精度,具体细节自己推一推. Code 因为是高精乘低精,高精

BZOJ1005: [HNOI2008]明明的烦恼

Description 自从明明学了树的结构,就对奇怪的树产生了兴趣...... 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? Input 第一行为N(0 < N < = 1000),接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1 Output 一个整数,表示不同的满足要求的树的个数,无解输出0 Sample Input 31-1-1 Sample Output 2 HINT 两棵树分别为1-2-3;1-3-2

【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 Outp

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