1. 程式人生 > >【bzoj1005】[HNOI2008]明明的煩惱 Prufer序列+高精度

【bzoj1005】[HNOI2008]明明的煩惱 Prufer序列+高精度

合數 ++ sizeof 數學 return prufer 情況 noi2008 con

題目描述

給出標號為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;
}

【bzoj1005】[HNOI2008]明明的煩惱 Prufer序列+高精度