Prufer 序列 学习笔记
定义
Prufer 序列的定义:
对于一个有根树,每次选择编号最小的叶子节点删掉,然后记录他的父亲,直到剩下两个节点,形成一个 \(n-2\) 长度的序列。
可以证明这个序列和一个 \(n\) 个节点的无根树一一对应。同时也可以证明一个 \(n\) 个节点完全图的生成树数量为 \(n^{n-2}\)。
由树构造 Prufer 序列
显然用堆可以 \(\Theta(n\log n)\)。
发现如果一个节点删除之后,如果他的父亲成为了新的叶子节点,并且编号比删除的节点小,那么接下来删除的肯定是他的父亲(因为其他的节点编号都比删除的节点大。这样就线性了。\(\Theta(n)\)。
由 Prufer 序列构造树
根据 Prufer 序列可以得到树的度数。用堆依旧是 \(\Theta(n\log n)\)。
考虑模仿树构造 Prufer 序列,如果序列中后一个数字比前一个数字更小,那么肯定就是父子关系,否则继续找最小的度数是 \(1\) 的节点,同样是线性的。
int n,opt,fa[maxn],p[maxn],d[maxn];
void S1(){//tree -> prufer
int i,j; ll ans=0; for(i=1;i<n;i++) fa[i]=read(),d[fa[i]]++;
for(i=1,j=1;i<=n-2;i++,j++){
while(d[j]) j++; p[i]=fa[j];
while(i<=n-2&&!--d[p[i]]&&p[i]<j) p[i+1]=fa[p[i]],i++;
} for(i=1;i<=n-2;i++) ans^=1ll*i*p[i]; print(ans); return;
}
void S2(){//prufer -> tree
int i,j; ll ans=0; for(i=1;i<=n-2;i++) p[i]=read(),d[p[i]]++; p[n-1]=n;
for(i=1,j=1;i<n;i++,j++){
while(d[j]) j++; fa[j]=p[i];
while(i<n&&!--d[p[i]]&&p[i]<j) fa[p[i]]=p[i+1],i++;
} for(i=1;i<n;i++) ans^=1ll*i*fa[i]; print(ans); return;
}
int main(){
freopen("1.in","r",stdin);
//freopen(".out","w",stdout);
n=read(); opt=read(); if(opt==1) S1(); else S2(); return 0;
}