P3224 [HNOI2012]永无乡 FHQ-Treap 启发式合并

该博客介绍了如何使用并查集维护岛屿间的连通性,并结合平衡树(如斐波那契堆)进行启发式合并,以解决在岛屿间修建桥梁的问题。博主分享了在实现过程中遇到的错误,即忘记更新节点,导致调试时间增加。代码中展示了并查集和平衡树的具体实现,包括连通块查询和合并操作,最后提供了完整的C++代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:
B   x   y B\ x\ y B x y 表示在岛 x x x 与岛 y y y 之间修建一座新桥。

Q   x   k Q\ x\ k Q x k 表示询问当前与岛 x x x 连通的所有岛中第 k k k 重要的是哪座岛,即所有与岛 x x x 连通的岛中重要度排名第 k k k 小的岛是哪座,请你输出那个岛的编号。

题解:
一眼题,用并查集维护一下连通块,平衡树合并时用启发式合并,合并最多也就 n l o g n nlogn nlogn次,总时间复杂度位 O ( n l o g n 2 ) O(nlogn^2) O(nlogn2)

就是写代码的时候跟失了智一样,忘加一句话多调试了三小时,愚不可及
代码:

#include<bits/stdc++.h>
#define endl '\n'
using namespace std;

const int maxn=5e5+10;

int a[maxn];
int f[maxn];
int ifind(int x){
    if(x==f[x])  return x;
    return f[x]=ifind(f[x]);
}
mt19937 rnd(233);

struct FHQ{
    int root[maxn],cnt;
    int ls[maxn],rs[maxn],val[maxn],key[maxn],siz[maxn];

    int newnode(int vals){
        val[++cnt]=vals;
        key[cnt]=rnd();
        siz[cnt]=1;
        return cnt;
    }
    void updata(int node){
        siz[node]=siz[ls[node]]+siz[rs[node]]+1;
    }
    void spilt_val(int node,int vals,int &x,int &y){
        if(!node){
            x=y=0;
            return;
        }
        if(val[node]<=vals){
            x=node;
            spilt_val(rs[node],vals,rs[node],y);
        }
        else{
            y=node;
            spilt_val(ls[node],vals,x,ls[node]);
        }
        updata(node);
    }
    int mer(int x,int y){
        if(!x||!y) return x+y;
        if(key[x]>key[y]){
            rs[x]=mer(rs[x],y);
            updata(x);
            return x;
        }
        else {
            ls[y]=mer(x,ls[y]);
            updata(y);
            return y;
        }
    }
    void mer_node(int tree,int nd){
        int x,y;
        spilt_val(root[tree],val[nd],x,y);
        root[tree]=mer(mer(x,nd),y);
    }
    int x;
    void dfs(int node){
        if(!node) return ;
        dfs(ls[node]);
        dfs(rs[node]);
        ls[node]=rs[node]=0;  //!!摘掉以后记得清空
        siz[node]=1;
        mer_node(x,node);
    }
//    void lrd(int node){
//        if(!node) return;
//        //cout<<"dfs "<<node<<endl;
//        lrd(ls[node]);
//        lrd(rs[node]);
//    }
    int merge_tree(int a,int b){
        if(siz[root[a]]<siz[root[b]]) swap(a,b);  //启发式,把b合并到a上
        x=a;
        dfs(root[b]);

        return x;
    }
    int get_num(int tree,int rk){
        int node=root[tree];
        if(siz[node]<rk) return -1;
        while(node){
            if(siz[ls[node]]+1==rk) break;
            else if(siz[ls[node]]>=rk) node=ls[node];
            else{
                rk-=siz[ls[node]]+1;
                node=rs[node];
            }
        }
        return node;
    }
}fhq;


signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    for(int i=1;i<=100000;i++){
        f[i]=i;
    }
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        fhq.root[i]=fhq.newnode(a[i]);
    }
    for(int i=1;i<=m;i++){
        int x,y;
        cin>>x>>y;
        int dx=ifind(x);
        int dy=ifind(y);
        if(dx!=dy){
            int now=fhq.merge_tree(dx,dy);
            f[dx]=now;
            f[dy]=now;
        }
    }
    int q;
    cin>>q;
    while(q--){
        char opt;
        cin>>opt;
        if(opt=='Q'){
            int x,k;
            cin>>x>>k;
            x=ifind(x);
            cout<<fhq.get_num(x,k)<<endl;

        }
        else{
            int x,y;
            cin>>x>>y;
            int dx=ifind(x);
            int dy=ifind(y);
            if(dx!=dy){
                int now=fhq.merge_tree(dx,dy);
                f[dx]=now;
                f[dy]=now;
            }
        }
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值