Prufer 序列
tip:
Prufer 序列将一棵树表示成了一个长度为 节点数 -2 的序列,且每个 Prufer 序列对应且只对应一棵树。
性质:
1、每个节点在 Prufer 序列中出现的次数就是 这个节点的度数 -1。
2、n 个点构成的无根树的个数: $n^{n-2}$
3、确定 n 个点度数分别为 d1,d2 … 时无根树个数:$(n-2)!/((d1-1)!*(d2-1)!…)$
4、 n 个点有标号的有根树的个数: $n*n^{n-2} = n^{n-1}$
实战:
T1:明明的烦恼
题干:
自从明明学了树的结构,就对奇怪的树产生了兴趣 ...... 给出标号为 1 到 N 的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树?
输入第一行为 N(0<N<=1000),接下来N行,第 i+1 行给出第i个节点的度数 Di ,如果对度数不要求,则输入 -1。
输出一个整数,表示不同的满足要求的树的个数,无解输出 0
题解:
这道题只给了一部分点的度数,无法直接用上面的性质,但可以稍变一下式。
当我们 n 个节点度数全部可知,解就为:
$\frac{(n-2)!}{\prod_{i=1}^n(d_i-1)!}$
但我们并没有全部知道,设已知序列长度为 $sum$ ,则有:
$sum=\sum_{i=1}^n{d_i-1}$
设我们知道的节点数为 $cnt$,则有:
$C_{n-2}^{sum}\frac{sum!}{\prod_{i=1}^n(d_i-1)!}$
我们还剩下 $n-cnt$ 个未知的:
$C_{n-2}^{sum}\frac{sum!}{\prod_{i=1}^n(d_i-1)!}\times (n-cnt)^{n-2-sum}$
化简可得:
$\frac{(n-2)!}{(n-2-sum)!\prod_{i=1}^n(d_i-1)!}\times (n-cnt)^{n-2-sum}$
最后打一个线筛,唯一分解就可以了。
Code:
1 #include<cstdio> 2 #include<cstring> 3 #define ll long long 4 #define $ 2100 5 using namespace std; 6 int m,n,k,t,c[$],maxx,lst[$],prime[$],cnt,sum,tot,a[$]; 7 bool judge[$],vis; 8 inline int max(int x,int y){ return x>y?x:y; } 9 inline void cut(int x,int add){ 10 if(x<1) return ; 11 while(x!=1) maxx=max(maxx,lst[x]), c[lst[x]]+=add, x/=lst[x]; 12 } 13 inline void eular(){ 14 lst[1]=1; 15 for(register int i=2;i<=n;++i){ 16 if(!judge[i]) lst[i]=i, prime[++cnt]=i; 17 for(register int j=1;j<=cnt&&i*prime[j]<=n;++j){ 18 judge[i*prime[j]]=1, lst[i*prime[j]]=prime[j]; 19 if(i%prime[j]==0) break; 20 } 21 } 22 } 23 signed main(){ 24 scanf("%d",&n); eular(); 25 for(register int i=2;i<=n-2;++i) cut(i,1); 26 for(register int i=1,x;i<=n;++i){ 27 scanf("%d",&x); 28 if(x!=-1){ 29 tot++, sum+=x-1; 30 for(register int j=2;j<=x-1;++j) cut(j,-1); 31 } 32 } 33 if(n-2-sum<0){ puts("0"); return 0; } 34 for(register int i=2;i<=n-2-sum;++i) cut(i,-1); 35 for(register int i=1;i<=n-2-sum;++i) cut(n-tot,1); 36 a[0]=a[1]=1; 37 ll yu=0; 38 for(register int i=2;i<=maxx;++i){ 39 if(c[i]<0){ puts("0"); return 0; } 40 for(register int j=1;j<=c[i];++j){ 41 for(register int k=1;k<=a[0];++k){ 42 a[k]=a[k]*i+yu; 43 yu=a[k]/10; a[k]%=10; 44 } 45 while(yu) a[++a[0]]=yu%10, yu/=10; 46 } 47 } 48 for(register int i=a[0];i;--i) printf("%d",a[i]); 49 }