prufer序列

 

$prufer$ 序列

 

  突然想起来自己还学过这么一个东西...

定义

  长度为 $n-2$ 的 $prufer$ 序列可以和 $n$ 个节点的有标号无根树形成一一映射;

构建方法

  每次删去当前编号最小的叶子 $x$,并在序列末尾加入与 $x$ 相连的点的编号;

还原方法

  1.找出1至n中没有在序列中出现的最小数 $x$。连接 $x$ 与序列首项,以后找最小值时不再考虑$x$,同时删去序列的首项。

  2.重复上述步骤,直到序列用尽;

  3.连接那两个没有作为最小值出现过的点;

一些性质

  1.对于一个度数为$x$的点,要删去$x-1$个它的相邻点后它才会被删去,所以它在prufer序列中出现的次数就是 $x-1$;

  2.n个点的带标号无根树一共有$n^{n-2}$种;

  3.对于有根树,在无根树的基础上再枚举一个根就好了,所以共有 $n^{n-1}$ 种;

几个例题

  树的计数:https://www.luogu.org/problem/P2290

  题意概述:已知每个点的度数,求有多少种满足条件的树;

  根据性质1,已经知道了每个点在prufer序列中的出现次数,接下来只要求一个可重排列就好了。

  $\frac{(n-2)!}{\prod(d_i-1)!}$

  
 1 # include <cstdio>
 2 # include <iostream>
 3 # define R register int
 4 
 5 using namespace std;
 6 
 7 const int N=160;
 8 int n,s;
 9 int d[N];
10 int v[N];
11 
12 void add (int x,int a)
13 {
14     for (R i=2;i<=x;++i)
15         while(x%i==0) v[i]+=a,x/=i;
16 }
17 
18 int main()
19 {
20     scanf("%d",&n);
21     for (R i=1;i<=n;++i)
22     {
23         scanf("%d",&d[i]);
24         if(n!=1&&d[i]==0)
25         {
26             printf("0");
27             return 0;
28         }
29         s+=d[i];
30     }
31     if(s!=2*(n-1)) { printf("0"); return 0; }
32     for (R i=1;i<=n-2;++i) add(i,1);
33     for (R i=1;i<=n;++i)
34         for (R j=1;j<=d[i]-1;++j)
35             add(j,-1);
36     long long ans=1;
37     for (R i=1;i<=n;++i)
38         for (R j=1;j<=v[i];++j)
39             ans=ans*i;
40     printf("%lld",ans);
41     return 0;
42 }
树的计数

 

  明明的烦恼:https://www.luogu.org/problem/P2624

  题意概述:有的点知道了度数,有的点不清楚,求有多少种满足条件的树;

  设有 $k$ 个点知道了度数,$n-k$个点不知道, $s$ 为所有已知度数的点出现的次数和。首先在 $n-2$ 个位置中找出 $s$ 个位置来填这些数,未确定的数可以随便填,所以答案就是:

  $\binom{n-2}{s}\frac{s!}{\prod(d_i-1)!}(n-k)^{n-2-s}$

  
 1 // luogu-judger-enable-o2
 2 # include <cstdio>
 3 # include <iostream>
 4 # define R register int
 5 
 6 using namespace std;
 7 
 8 const int N=1005;
 9 int n,s,cnt;
10 int v[N],d[N];
11 int ans[100*N];
12 
13 void add (int x,int a)
14 {
15     for (R i=2;i<=x;++i)
16         while(x%i==0) x/=i,v[i]+=a;
17 }
18 
19 void write()
20 {
21     for (R i=ans[0];i;i--)
22         printf("%d",ans[i]);
23 }
24 
25 
26 void mul (int x)
27 {
28        for (R i=1;i<=ans[0];++i) ans[i]*=x;
29     for (R i=1;i<=ans[0]+5;++i)
30     {
31         ans[i+1]+=ans[i]/10;
32         ans[i]%=10;
33     }
34     ans[0]+=5;
35     while(ans[ ans[0] ]==0&&ans[0]) ans[0]--;    
36 }
37 
38 int main()
39 {
40     scanf("%d",&n);
41     for (R i=1;i<=n;++i)
42     {
43         scanf("%d",&d[i]);
44         if(n==1)
45         {
46             if(d[1]==0) { printf("1"); return 0; }
47             else { printf("0"); return 0; }
48         }
49         if(d[i]==0&&n!=1) { printf("0"); return 0; }
50         if(d[i]!=-1) s+=d[i];
51         else cnt++;
52     }
53     if(cnt+s>2*(n-1)) { printf("0"); return 0; }
54     s=0; for (R i=1;i<=n;++i) if(d[i]!=-1) s+=d[i]-1;
55     for (R i=2;i<=n-2;++i) add(i,1);
56     for (R i=2;i<=n-2-s;++i) add(i,-1);
57     for (R i=1;i<=n;++i)
58     {
59         if(d[i]==-1) continue;
60         for (R j=2;j<=d[i]-1;++j) add(j,-1);
61     }
62     ans[0]=1; ans[1]=1;
63     for (R i=2;i<=n;++i)
64         for (R j=1;j<=v[i];++j)
65             mul(i);
66     for (R i=1;i<=n-2-s;++i)
67         mul(cnt);
68     write();
69     return 0;
70 }
明明的烦恼

 

  数树:https://www.luogu.org/problem/P5206

  先咕着,我就不信NOIP考这题。

---shzr

posted @ 2019-09-17 14:14  shzr  阅读(177)  评论(0编辑  收藏  举报