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)!} imes (n-cnt)^{n-2-sum}$

  化简可得:

$frac{(n-2)!}{(n-2-sum)!prod_{i=1}^n(d_i-1)!} imes (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 }
View Code
越努力 越幸运
原文地址:https://www.cnblogs.com/OI-zzyy/p/11235584.html