[BZOJ1005] HNOI2008明らかトラブル

問題の説明

明らかに学習されたツリーので、右奇妙生成関心のツリー......所与は、Nポイントに1の番号を付け、そしてせる最終度特定の点、
任意の2点間の接続であってもよいです何度ツリーの要件を満たすために生成されましたか?

入力形式

最初の動作N(0 <N <= 1000 )、
次のN行、行I + 1ジノードIの程度を与え、程度が必要とされない場合、入力-1

出力フォーマット

ツリー満たし異なる要件、無溶液出力の数を表す整数0

サンプル入力

3
1
-1
-1

サンプル出力

2

サンプルの解釈

2つのツリーは1-2-3であり、1-3-2

解決

次はPurferシーケンスを使用する必要があります。

Purferシーケンス

このツリー・シーケンスの次の原理に従って構成された無根ツリーがPurferシーケンスと呼ばれます。ツリーのサイズがNである場合、長さn-2のそのような配列。

原理:このポイントがツリーから削除されている間程度の現在のツリーで見つかった各は、ポイント1の最小数は、シーケンスのこの時点で彼の父親に参加します

この配列は、それぞれ固有のPurferシーケンス無根ツリーに対応するような特性の数を有しています。しかし、私たちが使用することは別の非常に重要な特性です。

ツリー内の各ノードの\(I \)の程度設け、\(D_I \) 次いで\(I \)を順に表示される\(D_I-1 \)回。

後にこの自然と、この質問を見てください。私たちはすでに次のように無根樹プログラムの数は条件を満たすように、各点の度合いを知っているとします。

\ [ANS = \ FRAC {(N-2)!} {\ prod_ {I = 1} ^ {N}(D_I-1)!} \]

これは、再配置される式です。しかし、今、いくつかのポイントが限定されるものではなく、配列の長さだけ\(\ 2-N-) だから、覚えています:

\ [CNT = \ sum_ ^ {I 1 =} N [D_I =! - 1] \\和= \ sum_ {I = 1} ^ N [D_I =! - 1] \回(D_I-1)\]

その後、プログラムはで正当なものである(N-2 \)\の位置選択\(SUM \) これらの位置を再配置することができます。休止位置であってもよい\(\ N-CNT)だけでポイントを置きます。プログラム番号:

\ [ANS = C_ {N-2} ^ {和} \ FRAC {和!} {\ prod_ {iは1 =} ^ N [D_I =! - 1] \回(D_I-1)} \倍(nは-cnt)^ {N-2-和} \]

簡素化を得ることができます。

\ [ANS = \ FRAC {(N-2)!} {(N-2-和)\回\ prod_ {I = 1} ^ N [D_I =! - 1] \回(D_I-1)!} \倍(N-CNT)^ {N-2-和} \]

もちろん、1はそう書かれても、高精度の乗算と除算を必要としています。私たちは、それが低精度の計算により、高精度で使用することができ、最終的な答えは、素因数逓倍形になり、素因数分解を使用することができます。

注、Purferシーケンスカウントは、ツリーの程度に関連する問題の多くを解決することができます。

PS:上記の説明単純すぎるのPurferシーケンスは、Baiduのために説明することができます

コード

#include <iostream>
#include <cstdio>
#define N 1000002
using namespace std;
int n,sum,cnt,i,j,k,d[N],p[N],c[N],num,ans[N],l;
bool vis[N];
int read()
{
	char c=getchar();
	int w=0,f=1;
	while(c<'0'||c>'9'){
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c<='9'&&c>='0'){
		w=w*10+c-'0';
		c=getchar();
	}
	return w*f;
}
void change(int x,int op)
{
	for(int i=1;i<=num;i++){
		while(x%p[i]==0) c[p[i]]+=op,x/=p[i];
	}
}
int main()
{
	n=read();
	for(i=2;i<=1000;i++){
		if(!vis[i]){
			p[++num]=i;
			for(j=i;j<=1000;j+=i) vis[j]=1;
		}
	}
	for(i=1;i<=n;i++){
		d[i]=read();
		if(d[i]!=-1){
			cnt++;sum+=d[i]-1;
			for(j=2;j<=d[i]-1;j++) change(j,-1);
		}
	}
	if(n==1){
		if(d[1]>0) puts("0");
		else puts("1");
		return 0;
	}
	else if(n==2){
		if((d[1]!=1&&d[1]!=-1)||(d[2]!=1&&d[2]!=-1)) puts("0");
		else puts("1");
		return 0;
	}
	for(i=1;i<=n;i++){
		if(d[i]==0||d[i]>=n){
			puts("0");
			return 0;
		}
	}
	for(i=2;i<=n-2;i++) change(i,1);
	for(i=2;i<=n-sum-2;i++) change(i,-1);
	for(i=1;i<=n-sum-2;i++) change(n-cnt,1);
	ans[1]=l=1;
	for(i=2;i<=1000;i++){
		for(j=1;j<=c[i];j++){
			int tmp=0;
			for(k=1;k<=l;k++){
				ans[k]=ans[k]*i+tmp;
				tmp=0;
				if(ans[k]>=10) tmp=ans[k]/10,ans[k]=ans[k]%10;
			}
			while(tmp) ans[++l]=tmp%10,tmp/=10;
		}
	}
	for(i=l;i>=1;i--) printf("%d",ans[i]);
	puts("");
	return 0;
}

おすすめ

転載: www.cnblogs.com/LSlzf/p/12623848.html