Prüfer 序列学习笔记
Prüfer 序列学习笔记
知识点
前言
Prüfer 序列是为了证明 Cayley 公式而被发明出来的,即一个 \(n\) 个点的完全图共有 \(n^{n-2}\) 个不同的树。
Prüfer 序列可以将一个 \(n\) 个点的树唯一映射到一个长度为 \(n-2\) 的序列上,即两棵树不同当且仅当它们的 Prüfer 序列不同。
Prüfer 序列的构造
每次找到编号最小的一个叶子节点,将与其相连的节点加入序列,并将其在树上删除,直到树上只有两个节点。

Prüfer 序列具有以下两个显然的性质:
- 树中最后剩下的两个点中,一定存在一个节点是编号最大的点 \(n\)
- 每个节点在序列中出现的次数为该节点的度数 \(-1\)。
Prüfer 序列的重构树
下面证明 Prüfer 序列可以唯一表示一棵树。
依次考虑 Prüfer 序列 \(p\) 中的每一个元素,假设当前考虑到第 \(i\) 个元素 \(p_i\),不难发现未被标记的编号最小的叶子节点是好确定的。在未被标记的节点中,编号最小的不在 \(p[i:n]\) 中出现的节点 \(x\) 就是当前未被标记的编号最小的叶子节点,将 \(x\) 标记为叶子节点。以此类推,可以确定 \(n-2\) 条边。而最后一条边的一个端点一定是 \(n\),另一个端点可以通过上面的方法确定。证毕。
存在一个更为优秀的线性做法。
令指针 \(x\) 指向编号最小的叶子节点,将其删除,删除该叶子节点后一定会使得一个节点的度数 \(-1\),若出现一个新的叶子节点 \(y\),且 \(y<x\),就继续删除,重复上述操作直至新出现的叶子节点比 \(x\) 大或没有新的叶子节点。
多次进行上面的操作就可以得到重构树。
例题
CF156D Clues
小 D 最近在幼儿园学习了图论知识。
他知道对于一个 \(n\) 个点 \(m\) 条边的带标号无向图 \(G\),若形成了 \(k\) 个连通块,最少增加 \(k-1\) 条边就可以使得图 \(G\) 连通。
但是小 D 突发奇想,他想知道增加 \(k-1\) 条边使得整个图连通的方案数对他喜欢的模数 \(p\) 取模的结果。
对于全部数据,\(1\leq n,m\leq 10^5\),\(1\leq p\leq 10^9\)。
设增加 \(k-1\) 条边之后,第 \(i\) 个连通块的度数为 \(d_i\)。
对于给定序列 \(\{d_i\}\),Prüfer 序列的数量为
其中,\(s_i\) 表示第 \(i\) 个连通块中的节点数量。
对于给定序列 \(\{d_i\}\),使图连通的方案数为
枚举序列 \(\{d_i\}\),使图连通的方案数为
令 \(e_i=d_i-1\):$$\sum\limits_{e_i\geq 0,\sum_{i=1}^k e_i=k-2}\dbinom {k-2}{e_1,e_2,\cdots,e_k}\prod\limits_{i=1}^k s_i^{e_i+1}$$
考虑多元二项式定理:
可以将原式变为
点击查看代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 1000010
int n,m,mod;
int fa[N],siz[N];
int read ()
{
int k=1,s=0;char ch=getchar ();
while (!isdigit (ch)) {if (ch=='-') k=-1;ch=getchar ();}
while (isdigit (ch)) {s=s*10+ch-'0';ch=getchar ();}
return k*s;
}
int Qpow (int x,int y)
{
int res=1;
while (y>0)
{
if (y&1) res=1ll*res*x%mod;
x=1ll*x*x%mod,y>>=1;
}
return res;
}
int Find (int x)
{
return x==fa[x]?x:fa[x]=Find (fa[x]);
}
void Merge (int x,int y)
{
int rx=Find (x),ry=Find (y);
if (rx!=ry)
{
fa[ry]=rx;
siz[rx]+=siz[ry];
}
}
void Init ()
{
n=read (),m=read (),mod=read ();
for (int i=1;i<=n;i++)
fa[i]=i,siz[i]=1;
for (int i=1;i<=m;i++)
{
int x=read (),y=read ();
Merge (x,y);
}
}
void Work ()
{
int cnt=0,ans=1;
for (int i=1;i<=n;i++)
if (i==Find (i))
{
cnt++;
ans=1ll*ans*siz[i]%mod;
}
if (cnt==1) ans=1%mod;
else ans=1ll*ans*Qpow (n,cnt-2)%mod;
printf ("%d\n",ans);
}
signed main ()
{
Init ();
Work ();
return 0;
}
AT_arc162_d [ARC162D] Smallest Vertices
给定一个使得其总和为 \(N-1\) 的非负整数序列 \(d=(d_1,d_2,\ldots,d_N)\)。
对于带编号从 \(1\) 到 \(N\) 的顶点,假设 \(1\) 是根,我们将其点度数定义为 \(d_i\)。
我们称满足以下条件的 根向树 为好树:
\(•\) 点 \(i\) 的出度是 \(d_i\)。
此外,对于好树的顶点 \(v\),定义 \(f(v)\) 为以 \(v\) 为根的子树中(包括 \(v\))顶点编号的最小值。我们将满足 \(f(v)=v\) 的顶点称为好顶点。
求好树中所有好顶点的总数,将其对 \(998244353\) 取模后的余数。
对于全部数据,\(2 \leq N \leq 500\),\(0 \leq d_i \leq N-1\),\(d_1 \geq 1\),\(\sum_{i=1}^N d_i=N-1\)。
点击查看代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 505
const int mod=998244353;
int n,deg[N];
int fac[N],ifac[N],F[N][N][N];
int read ()
{
int k=1,s=0;char ch=getchar ();
while (!isdigit (ch)) {if (ch=='-') k=-1;ch=getchar ();}
while (isdigit (ch)) {s=s*10+ch-'0';ch=getchar ();}
return k*s;
}
int Qpow (int x,int y)
{
int res=1;
while (y>0)
{
if (y&1) res=1ll*res*x%mod;
x=1ll*x*x%mod,y>>=1;
}
return res;
}
void Init ()
{
n=read ();
fac[0]=1;
for (int i=1;i<=n;i++)
fac[i]=1ll*fac[i-1]*i%mod;
ifac[n]=Qpow (fac[n],mod-2);
for (int i=n-1;i>=0;i--)
ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
for (int i=1;i<=n;i++)
deg[i]=read ();
}
void Work ()
{
F[n+1][0][0]=1;
for (int i=n;i>=1;i--)
for (int j=0;j<=n-i;j++)
for (int k=0;k<n;k++)
{
F[i][j][k]=(F[i][j][k]+F[i+1][j][k])%mod;
F[i][j+1][k+deg[i]]=(F[i][j+1][k+deg[i]]+F[i+1][j][k])%mod;
}
int res=fac[n-2],ans=0;
for (int i=1;i<=n;i++)
if (i==1) res=1ll*res*ifac[deg[i]-1]%mod;
else res=1ll*res*ifac[deg[i]]%mod;
for (int i=1;i<=n;i++)
{
if (i==1 || deg[i]==0)
{
ans=(ans+res)%mod;
continue;
}
for (int j=deg[i]+1;j<=n-i+1;j++)
ans=(ans+1ll*F[i+1][j-1][j-1-deg[i]]*deg[i]%mod*fac[j-2]%mod*fac[n-j-1]%mod*ifac[n-2]%mod*res%mod)%mod;
}
printf ("%d\n",ans);
}
signed main ()
{
Init ();
Work ();
return 0;
}

浙公网安备 33010602011771号