bzoj2733 永无乡 splay树的启发式合并

https://vjudge.net/problem/HYSBZ-2733

给一些带权点,有些点是互相连通的,

然后给出2种操作,在两点间加一条边,或者询问一个点所在的连通块内的第k小值的编号

并查集辅助+splay的启发式合并就行

由于结构简单,动态开点线段树合并也可以做

我写的是splay,由于一个奇怪的bug,我一气之下把之前的核心代码里的我自己写的splay和rotate代码全换成板子了

#include <bits/stdc++.h>
#define endl ‘\n‘
#define ll long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
using namespace std;
const int maxn=1e6+10,maxm=2e6+10;
const int INF=0x3f3f3f3f;
int casn,n,m,k;
vector<int> pre;
int find(int now) {return pre[now]==now?now:pre[now]=find(pre[now]);}
vector<int> val;
class splaytree{public:
#define nd node[now]
#define ndl node[node[now].son[0]]
#define ndr node[node[now].son[1]]
    struct splaynode{
        int son[2],fa,val,size;
        splaynode(){size=1,fa=son[0]=son[1]=0;}
    };
    int cnt,root;
    vector<splaynode> node;
    inline void pushup(int now){nd.size=ndl.size+ndr.size+1;}
    inline void pushdown(int now){}
    inline int wh(int now){return node[nd.fa].son[1]==now;}
    void rotate(int now){
        int fa=nd.fa,gf=node[fa].fa,c=wh(now);
        pushdown(fa);pushdown(now);
        if(gf) node[gf].son[wh(fa)]=now;
        nd.fa=gf;
        node[fa].son[c]=nd.son[c^1];
        node[node[fa].son[c]].fa=fa;nd.son[c^1]=fa;node[fa].fa=now;
        pushup(fa);pushup(now);
    }
    void splay(int now,int dst=0){
        for(;nd.fa!=dst;rotate(now))
            if(node[nd.fa].fa!=dst)rotate(wh(now)==wh(nd.fa)?nd.fa:now);
            if(!dst) root=now;
    }
    void insert(int pos){
        int now=root,fa=0,val=node[pos].val;
        while(now) fa=now,now=val<nd.val?nd.son[0]:nd.son[1];
        now=pos;
        node[fa].son[val>node[fa].val]=now;
        nd.fa=fa;
        splay(now);
    }
    void order(int now){
        int l=nd.son[0],r=nd.son[1];
        nd.son[0]=nd.son[1]=nd.fa=0;
        nd.size=1;
        if(l) order(l);
        insert(now);
        if(r) order(r);
    }
    void merge(int a,int b){
        if(a==b) return ;
        splay(a);splay(b);
        if(node[a].size>node[b].size) swap(a,b);
        pre[a]=b;root=b;
        order(a);
    }
    int kth(int now,int k){
        splay(now);int lsize=0;
        while(now){
            int lsum=lsize+ndl.size;
            if(k<=lsum) now=nd.son[0];
            else if(k==lsum+1) return now;
            else lsize=lsum+1,now=nd.son[1];
        }
        return -1;
    }
    splaytree(int n){
        node.resize(n+7,splaynode());
        rep(i,1,n) node[i].val=val[i];
        node[0].size=0;
        root=0,cnt=0;
    }
};
int main() {
    IO;
    int a,b;
    cin>>n>>m;
    val.resize(n+2);
    pre.resize(n+2);
    rep(i,1,n) cin>>val[i],pre[i]=i;
    splaytree tree(n);
    while(m--) {
        cin>>a>>b;
        a=find(a),b=find(b);
        tree.merge(a,b);
    }
    cin>>m;string s;
    while(m--){
        cin>>s>>a>>b;
        if(s=="B") {
						a=find(a),b=find(b);
            tree.merge(a,b);
        }else{
						a=find(a);
            cout<<tree.kth(a,b)<<endl;
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/nervendnig/p/10358664.html

时间: 2024-10-06 22:33:41

bzoj2733 永无乡 splay树的启发式合并的相关文章

BZOJ2733 永无乡【splay启发式合并】

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/转载请注明出处,侵权必究,保留最终解释权! Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含

[BZOJ2733] [HNOI2012] 永无乡 (splay启发式合并)

Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的.现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥.Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k

【bzoj2733】[HNOI2012]永无乡 线段树合并

Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的.现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥.Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k

[bzoj2733]永无乡&amp;&amp;[bzoj3545]Peaks

并不敢说完全会了线段树合并,只是至少知道原理写法了...还是太菜了,每天被大佬吊锤qwq 我看到的几道线段树合并都是权值线段树的合并.这个算法适用范围应该只是01线段树的. 这两道算入门题了吧... 发现粘题面没人看(自己都懒得看),以后粘链接加题意吧. 永无乡 给$n$个没有连边的带权点,动态加边,询问$u$所在连通块权值第$k$大的点是什么.$n \leq 1e5 , q\leq 3e5$ 离线永无乡?? 给定森林,点有点权有重复!,边有边权.询问$u$所在连通块,只能走边权小于$w$的边,

【BZOJ2733】永无乡[splay启发式合并or线段树合并]

题目大意:给你一些点,修改是在在两个点之间连一条无向边,查询时求某个点能走到的点中重要度第k大的点.题目中给定的是每个节点的排名,所以实际上是求第k小:题目求的是编号,不是重要度的排名.我一开始差点被这坑了. 网址:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 这道题似乎挺经典的(至少我看许多神犇很早就做了这道题).这道题有两种写法:并查集+(splay启发式合并or线段树合并).我写的是线段树合并,因为--splay不会打+懒得学.

bzoj2733 永无乡 平衡树按秩合并

链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 题意:动态连边,求出某个联通块中权值第$k$小的点. 首先,看到名次果断想平衡树……查询这个问题很好解决,但是合并……恐怕只能暴力修改了吧…… 这时候我们需要一个武器:启发式合并,通俗的讲就是小的插到大的里面去突然污了起来.我们可以想象一下,如果把大的那棵树合并到小的那棵去,那么每个节点暴力合并……时间代价不堪设想……因此按秩合并可以有效减短合并时间……然后就是暴力插点删点就行了……

bzoj2733: [HNOI2012]永无乡(splay)

2733: [HNOI2012]永无乡 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3778  Solved: 2020 Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的.现在有两

Bzoj 2733: [HNOI2012]永无乡 数组Splay+启发式合并

2733: [HNOI2012]永无乡 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3955  Solved: 2112[Submit][Status][Discuss] Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达

BZOJ 2733: [HNOI2012]永无乡(treap + 启发式合并 + 并查集)

不难...treap + 启发式合并 + 并查集 搞搞就行了 ---------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #define rep(i, n) for(int i = 0; i &l