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

【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=2733

【题目大意】

  给出n个点,每个点都有自己的重要度,现在有连边操作和查询操作,
  查询操作要求找出一个连通块中重要度第k的点的id

【题解】

  我们用Treap维护每个连通块,对于连边操作,我们用启发式合并,
  将size比较小的Treap并入size比较大的Treap,同时用并查集维护连通信息

【代码】

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=100010,M=N;
namespace Treap{
    struct node{
        int val,cnt,size,p,id;node *l,*r;
        node(){}
        node(int val,int id):val(val),id(id){p=rand();cnt=size=1;l=r=NULL;}
        void up(){size=cnt;if(l)size+=l->size;if(r)size+=r->size;}
    }*root[N],*pool[N];
    int top;
    node *new_node(){return pool[top++];}
    void Initialize(){top=0;for(int i=0;i<N;i++)pool[i]=new node();}
    void Rotatel(node*&x){node*y=x->r;x->r=y->l;x->up();y->l=x;y->up();x=y;}
    void Rotater(node*&x){node*y=x->l;x->l=y->r;x->up();y->r=x;y->up();x=y;}
    //往treap上插入点
    void Insert(node*&x,node y){
        if(!x){x=new_node();(*x)=y;return;}
        x->size++;
        if(y.val==x->val){x->cnt++;return;}
        if(y.val<x->val){
            Insert(x->l,y);
            if(x->l->p>x->p)Rotater(x);
        }else{
            Insert(x->r,y);
            if(x->r->p>x->p)Rotatel(x);
        }
    }
    // 查找第k小的元素
    int kth(node*x,int rnk){
        while(x){
            int d=x->l?x->l->size:0;
            if(rnk<=d)x=x->l;
            else if(rnk>d+x->cnt)rnk-=d+x->cnt,x=x->r;
            else return x->id;
        }return -1;
    }
    void Del_node(node*&u){pool[--top]=u;u=NULL;}
    // 将B树并入A树
    void merge(node*&A,node*&B){
        if(!B)return;
        if(B->l)merge(A,B->l);
        if(B->r)merge(A,B->r);
        Insert(A,*B); Del_node(B);
    }
}
int n,m,k,u,v,q;
namespace Union_Find_Set{
    int f[M],size[M],block;
    void Initialize(){
        memset(f,0,sizeof(f));
        block=n;
    }
    int Find(int x){
        if(!f[x])f[x]=x,size[x]=1;
        if(f[x]==x)return x;
        return f[x]=Find(f[x]);
    }
    void Union(int x,int y){
        x=Find(x); y=Find(y);
        if(x==y)return;
        if(size[x]>size[y])swap(x,y);
        f[x]=y; size[y]+=size[x];
        block--;
    }
}
void Heuristic_merge(int x,int y){
    int fx=Union_Find_Set::Find(x);
    int fy=Union_Find_Set::Find(y);
    if(fx==fy)return;
    if(Treap::root[fx]->size<Treap::root[fy]->size)swap(x,y),swap(fx,fy);
    Treap::merge(Treap::root[fx],Treap::root[fy]);
    Union_Find_Set::f[fy]=fx;
}
int main(){
    using namespace Treap;
	Initialize();
	Union_Find_Set::Initialize();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&k);
        Union_Find_Set::f[i]=i;
        Insert(root[i],node(k,i));
    }
    while(m--){
        scanf("%d%d",&u,&v);
        Heuristic_merge(u,v);
    }
    scanf("%d",&q);
    char op[5];
    while(q--){
        scanf("%s",op);
        if(op[0]==‘B‘){
            scanf("%d%d",&u,&v);
            Heuristic_merge(u,v);
        }else{
            scanf("%d%d",&u,&v);
            printf("%d\n",kth(root[Union_Find_Set::Find(u)],v));
        }
    }return 0;
}
时间: 2024-10-07 07:34:39

BZOJ 2733 [HNOI2012]永无乡(启发式合并+Treap+并查集)的相关文章

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

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

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]永无乡 离线+主席树

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

BZOJ 2733 HNOI2012 永无乡 Treap+启发式合并

题目大意:给定一个无向图以及n个点的排名,多次连接一条边,多次求某个点所在联通块中排名第k小的点的编号 初始对于每个点建立一棵只有一个节点的Treap,然后每次连接两个点,利用并查集找到两个点的根节点,将size较小的Treap暴力拆解插入大的中,然后将小的并查集合并到大的中 今天下午各种脑残,一个小小的Treap改了不下10遍0.0 快去喝脑白金0.0 #include<cstdio> #include<cstring> #include<iostream> #inc

bzoj 2733: [HNOI2012]永无乡

23333用并查集维护联通,然后网上搞splay就行啦. %高端splay写法(2333写在struct里了,是不是叫构造函数什么的??) 1 #include<bits/stdc++.h> 2 #define N 100005 3 #define LL long long 4 #define ls tr[x][0] 5 #define rs tr[x][1] 6 using namespace std; 7 inline int ra() 8 { 9 int x=0,f=1; char ch

2733: [HNOI2012]永无乡

题解: 爬到了bzoj的数据哈哈哈哈 然后提交上去t了 自己测只有1秒多呀 不理解 然后这题目就是个线段树/平衡树合并裸题 来练一下线段树合并 据说是nlogn的 #include <bits/stdc++.h> using namespace std; const int N=2e5; #define rg register #define IL inline int n,m,fa[N],a[N],b[N],cnt,root[N],count2[N*15],ls[N*15],rs[N*15]

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 是连 通的.现在有两

HNOI2012永无乡

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

[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