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

BZOJ 2733: [HNOI2012]永无乡

题目概述:

n座岛屿,给出m座桥连接不同的两座岛屿.
现在有两种操作:
1.在两座不同岛屿间架设一座桥梁.
2.询问某一岛屿与之相连的岛屿(包括该岛屿)中重要度第k小的岛屿编号,若不存在,为-1.
输出所有第二种操作的答案.

题目分析:

先建n棵Treap实现第二种操作.
对于两棵Treap的合并,采用启发式合并,选择将节点数少的一个一个插入到节点数多的.既然要将一棵树的所有结点插到另一棵树里,可以选择dfs遍历一遍.
再用并查集维护每个结点所在集合.

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

const int maxn=100000+10;

char readchar() {
    char ch=getchar();
    while(ch<'A'||ch>'Z') ch=getchar();
    return ch;
}

struct Node {
    Node* ch[2];
    int key,rnd,sz,id;
    Node(){}
    Node(int key,int id):key(key),id(id){rnd=rand();sz=1;ch[0]=ch[1]=NULL;}
    void update() {
        sz=1;
        if(ch[0]) sz+=ch[0]->sz;
        if(ch[1]) sz+=ch[1]->sz;
    }
}*root[maxn],*pool[maxn];

int top;

void init() {top=0;for(int i=0;i<maxn;i++) pool[i]=new Node();}
Node* new_node() {return pool[top++];}
void del_node(Node* &u) {pool[--top]=u;u=NULL;}

void rotate(Node* &u,int d) {
    Node* v=u->ch[d^1];u->ch[d^1]=v->ch[d];v->ch[d]=u;
    u->update();(u=v)->update();
}

void insert(Node* &u,Node v) {
    if(!u) {
        u=new_node();(*u)=v;
        return ;
    }
    int d=v.key>u->key;
    insert(u->ch[d],v);++(u->sz);
    if(u->rnd>u->ch[d]->rnd) rotate(u,d^1);
}

#define lc (u->ch[0])
#define rc (u->ch[1])

int find_id(Node* u,int rnk) {
    while(u) {
        int d=lc?lc->sz:0;
        if(rnk<=d) u=lc;
        else if(rnk>d+1) rnk-=d+1,u=rc;
        else return u->id;
    }
    return -1;
}

int fa[maxn];

int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);}

void dfs(Node* &A,Node* &B) {//dfs遍历一遍B树,将其所有结点插入A树 
    if(!B) return ;
    if(B->ch[0]) dfs(A,B->ch[0]);
    if(B->ch[1]) dfs(A,B->ch[1]);
    insert(A,*B);del_node(B);
}

void merge(int x,int y) {//启发式合并 
    int fx=find(x),fy=find(y);
    if(fx==fy) return ;
    if(root[fx]->sz<root[fy]->sz) swap(x,y),swap(fx,fy);
    dfs(root[fx],root[fy]);fa[fy]=fx;
}

int main() {
    init();
    int n,m,k,u,v,q;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) {
        scanf("%d",&k);fa[i]=i;
        insert(root[i],Node(k,i));
    }
    while(m--) {
        scanf("%d%d",&u,&v);
        merge(u,v);
    }
    scanf("%d",&q);
    while(q--) {
        if(readchar()=='B') {
            scanf("%d%d",&u,&v);
            merge(u,v);
        } else {
            scanf("%d%d",&u,&v);
            printf("%d\n",find_id(root[find(u)],v));
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值