prufer 序列浅谈

prufer 序列完成了从一棵大小为 \(n\) 的无根树到长度为 \(n-2\) 的序列的双射,下面简述其构造过程:

从一棵无根树到 prufer 序列:

我们找到其编号最小的叶子,然后删掉叶子,把其父亲加入队列。重复操作,直到整棵树剩下两个节点。

\(O(n\log n)\) 是显然可以做的,我们考虑如何 \(O(n)\)。

用一个指针,指向当前编号最小的叶子节点,然后删掉他,如果其父亲变成了叶子,并且编号比它小,我们就删掉其父亲并重复操作,否则我们往后推指针,找到下一个叶子。

从 prufer 序列到一棵无根树

容易发现,每个节点的度数是在 prufer 序列中的出现次数加 \(1\),考虑我们现在知道每个点的度数,然后我们找到编号最小的叶子,然后删掉它,如果其父亲也变成了叶子,并且编号较小,我们重复这个操作,否则的话,我们考虑往后推指针,找到下一个叶子。最后会剩下两个点,其中一个节点是 \(n\),我们只需要在 prufer 序列最后加入 \(n\) 就可以避免特判。

代码:


#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define int long long
#define ull unsigned long long
#define mset(a,b) memset(a,b,sizeof(a))
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define N 5000100
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int n,m,d[N];

namespace Subtask1{

    int k=INF,fa[N],p[N];

    inline void Solve(){
        for(int i=1;i<=n-1;i++){
            int x;read(x);
            d[x]++;d[i]++;fa[i]=x;
        }
        for(int i=1,j=1;i<=n-2;i++,j++){
            while(d[j]!=1) j++;p[i]=fa[j];d[j]--;d[fa[j]]--;
            while(i<=n-2&&d[p[i]]==1&&p[i]<j){p[i+1]=fa[p[i]];d[p[i]]--;d[fa[p[i]]]--;i++;}
        }
        int ans=0;
        for(int i=1;i<=n-2;i++) ans=(ans^(i*p[i]));
        printf("%lld\n",ans);
    }
}

namespace Subtask2{

    int fa[N],p[N],d[N];

    inline void Solve(){
        for(int i=1;i<=n-2;i++) read(p[i]),d[p[i]]++;
        p[n-1]=n;
        for(int i=1;i<=n;i++) d[i]++;
        for(int i=1,j=1;i<=n-1;i++,j++){
            while(d[j]!=1) j++;fa[j]=p[i];d[j]--;d[p[i]]--;
            while(i<=n-1&&d[p[i]]==1&&p[i]<j){fa[p[i]]=p[i+1];d[p[i]]--;d[fa[p[i]]]--;i++;}
        }
        int ans=0;
        for(int i=1;i<=n-1;i++) ans=(ans^(i*fa[i]));
        printf("%lld\n",ans);
    }
}

signed main(){
    read(n);read(m);
    if(m==1) Subtask1::Solve();else Subtask2::Solve();
    return 0;
} 

上一篇:Linux命令


下一篇:约数个数(牢记公式)