【洛谷P3224】永无乡 并查集+Splay启发式合并

题目大意:给定 N 个点的图,点有点权,初始有一些无向边,现在有 Q 个询问,每个询问支持动态增加一条无向边连接两个不连通的点和查询第 X 个点所在的联通块中权值第 K 大的是哪个点。

题解:学会了平衡树的启发式合并。
以每个点建立一棵平衡树,需要加边时则合并两个点对应的平衡树,启发式的思想在于合并的时候将 size 小的平衡树信息合并到 size 大的树上。这样,对于每一个被合并的点来说,其所在的平衡树的大小必定翻倍,而最极端的话,所有的点都在一个平衡树中,size = n,因此,每个点被合并的次数不超过 \(O(logn)\) 次,即:新建的节点数不超过 \(O(nlogn)\) 个。
需要注意的是,对于 Splay 来说,若对于以前的单个平衡树来说,根节点的父节点可以取 0,但是有 N 棵平衡树的话,每棵平衡树根节点的父节点不能相同,因此,将 tot 初始化成 N,对于第 i 个平衡树的根节点,将 i 作为其父节点,并且根节点需要手动初始化,不能 insert。
连通性什么的直接并查集维护就好喽。

代码如下

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;

int n,m,q,f[maxn],mp[maxn];
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
struct node{
    #define ls(x) t[x].ch[0]
    #define rs(x) t[x].ch[1]
    int ch[2],fa,val,size;
}t[maxn<<3];
int tot,root[maxn];
inline void pushup(int o){t[o].size=t[ls(o)].size+t[rs(o)].size+1;}
inline bool get(int o){return o==rs(t[o].fa);}
inline void rotate(int o){
    int fa=t[o].fa,gfa=t[fa].fa;
    int d1=get(o),d2=get(fa);
    t[fa].ch[d1]=t[o].ch[d1^1],t[t[o].ch[d1^1]].fa=fa;
    t[fa].fa=o,t[o].ch[d1^1]=fa;
    t[gfa].ch[d2]=o,t[o].fa=gfa;
    pushup(fa),pushup(o);
}
inline void splay(int o,int goal){
    while(t[o].fa!=goal){
        int fa=t[o].fa,gfa=t[fa].fa;
        if(gfa!=goal)get(o)==get(fa)?rotate(fa):rotate(o);
        rotate(o);
    }
    if(goal<=n)root[goal]=o;
}
void insert(int val,int i){
    int o=root[i],fa=0;
    while(o)fa=o,o=t[o].ch[t[o].val<val];
    o=++tot;
    if(fa)t[fa].ch[t[fa].val<val]=o;
    t[o].fa=fa,t[o].val=val,t[o].size=1;
    splay(o,i);
}
int kth(int o,int k){
    if(k<=t[ls(o)].size)return kth(ls(o),k);
    else if(k>t[ls(o)].size+1)return kth(rs(o),k-t[ls(o)].size-1);
    else return t[o].val;
}
void dfs(int o,int i){
    if(ls(o))dfs(ls(o),i);
    if(rs(o))dfs(rs(o),i);
    insert(t[o].val,i);
}
void merge(int a,int b){
    if(!a||!b)return;
    int x=find(a),y=find(b);
    if(x==y)return;
    if(t[root[x]].size>t[root[y]].size)swap(x,y);
    f[x]=y;
    dfs(root[x],y);
}

void read_and_parse(){
    scanf("%d%d",&n,&m),tot=n;
    for(int i=1,val;i<=n;i++){
        scanf("%d",&val);
        f[i]=i,mp[val]=i;
        root[i]=++tot,t[root[i]].fa=i,t[root[i]].size=1,t[root[i]].val=val;
    }
    for(int i=1,a,b;i<=m;i++){
        scanf("%d%d",&a,&b);
        merge(a,b);
    }
}

void solve(){
    char opt[2];int x,y;
    scanf("%d",&q);
    while(q--){
        scanf("%s%d%d",opt,&x,&y);
        if(opt[0]=='B')merge(x,y);
        else{
            if(y>t[root[find(x)]].size)puts("-1");
            else printf("%d\n",mp[kth(root[find(x)],y)]);
        }
    }
}

int main(){
    read_and_parse();
    solve();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wzj-xhjbk/p/10466848.html
今日推荐