1. 程式人生 > 實用技巧 >【洛谷P2290】樹的計數

【洛谷P2290】樹的計數

題目

題目連結:https://www.luogu.com.cn/problem/P2290
一個有 \(n\) 個節點的樹,設它的節點分別為 \(v_1,v_2,\ldots,v_n\),已知第 \(i\) 個節點 \(v_i\) 的度數為 \(d_i\),問滿足這樣的條件的不同的樹有多少棵。
\(1\le n\le 150\),保證滿足條件的樹不超過 \(10^{17}\) 個。

思路

既然點有了度數限制,那麼等價於 prufer 序列上這個點出現次數必須是 \(\mathrm{deg}_i-1\)
問題轉化為一個長度為 \(n-2\) 的序列,\(i\) 出現次數為 \(\mathrm{deg}_i-1\)

的方案數。顯然等於

\[\frac{(n-2)!}{\Pi^{n}_{i=1}(\mathrm{deg}_i-1)!} \]

由於題目保證了答案不超過 long long 範圍,那麼直接分解質因數最後再乘起來即可。
時間複雜度 \(O(n^2)\)

程式碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=155;
int n,deg[N],cnt[N][N],a[N];
ll ans;

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		int p=i;
		for (int j=2;j<=n;j++)
		{
			cnt[i][j]=cnt[i-1][j];
			for (;p%j==0;p/=j) cnt[i][j]++;
		}
	}
	for (int j=1;j<=n;j++)
		a[j]+=cnt[n-2][j];
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&deg[i]);
		if (!deg[i]) return printf("%d",deg[1]==0),0;
		for (int j=1;j<=n;j++)
			a[j]-=cnt[deg[i]-1][j];
		deg[0]+=deg[i]-1;
	}
	if (deg[0]!=n-2) return printf("0"),0;
	ans=1;
	for (int i=1;i<=n;i++)
		for (;a[i];a[i]--) ans*=i;
	printf("%lld",ans);
	return 0;
}