1. 程式人生 > 其它 >luogu P2290 [HNOI2004]樹的計數

luogu P2290 [HNOI2004]樹的計數

題面傳送門
首先這種生成樹計數想到Prufer序列。
然後因為度數為\(d_i\)的點會在Prufer序列中出現\(d_i-1\)次所以直接重排列公式即可。
然而這個沒有模數什麼的。
於是就要分解完質因數後再乘。
時間複雜度\(O(nlogn)\)
code:

#include <vector>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#include<bitset>
#include<set>
#include<map>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define N 150
#define M 100000
#define eps (1e-5)
#define mod (1<<31)
#define U unsigned int
using namespace std;
int n,x,A[N+5],G[N+5],fl[N+5],pre[N+5],pr[N+5],ph,ToT; ll Ans=1;
int main(){
	freopen("1.in","r",stdin);
	re int i,j;scanf("%d",&n);if(n==1){scanf("%d",&x);printf("%d\n",x?0:1);return 0;}
	A[n-2]++;for(i=2;i<=n;i++){
		!fl[i]&&(pr[++ph]=i,pre[i]=i);for(j=1;j<=ph&&pr[j]*i<=n;j++){fl[pr[j]*i]=1;pre[pr[j]*i]=pr[j];if(i%pr[j]==0) break;}
	}
	for(i=1;i<=n;i++){scanf("%d",&x),A[x-1]--,ToT+=x;if(!x){printf("0\n");return 0;}}if(ToT!=2*n-2){printf("0\n");return 0;}
	for(i=n;i;i--) A[i]+=A[i+1];
	for(i=1;i<=n;i++){x=i;while(x^1)G[pre[x]]+=A[i],x/=pre[x];}
	for(i=1;i<=n;i++) while(G[i]--) Ans*=i;printf("%d\n",Ans);
}