bzoj 2733 [HNOI2012]永无乡

来源:互联网 发布:拍拍贷淘宝买满标流程 编辑:程序博客网 时间:2024/06/05 11:23

传送门


Sol

  • 一眼看上去是水题,没想到跪了一上午。。。
  • 看到第k小想到平衡树,维护连通性用并查集。因此主要思路就是维护一个平衡树森林,合并操作就把两个平衡树合并
  • 难点在于,合并暴力肯定TLE,因此需要启发式合并
void dfs(int p,int &f){    if(lc) dfs(lc,f);    if(rc) dfs(rc,f);    int x,y;    split(f,val[p],x,y);    renode(p);    f=merge(merge(x,p),y);}
  • 代码是fhq treap。虽然就是把一颗平衡树拆掉再一个一个加到另一颗树里。但是启发式合并的巧妙在于,它是把节点数小的树拆掉合并到大的树上,于是均摊复杂度就成了优雅的O(nlogn)
  • 合并的时候还有一些细节:节点信息的清空和并查集的更新

Code

// by spli#include<cstring>#include<cstdio>#include<iostream>#include<algorithm>#include<ctime>#define lc ch[p][0]#define rc ch[p][1]using namespace std;const int N=100010*2;int n,m,q,sz;int c[N],pos[N];int lnk[N];int siz[N];int col[N],rt;int ch[N][2];int pri[N];int val[N];int _find(int x){    if(x!=lnk[x]) lnk[x]=_find(lnk[x]);    return lnk[x];}int newnode(int id){    sz++;    siz[sz]=1;    val[sz]=c[id];    pos[sz]=id;    pri[sz]=rand();    return sz;}void pushup(int p){    siz[p]=1;    if(lc) siz[p]+=siz[lc];    if(rc) siz[p]+=siz[rc];}int merge(int x,int y){    if(!x||!y) return x+y;    if(pri[x]<pri[y]){        ch[x][1]=merge(ch[x][1],y);        pushup(x);        return x;    }    else{        ch[y][0]=merge(x,ch[y][0]);        pushup(y);        return y;    }}void split(int p,int v,int &x,int &y){    if(!p){        x=0,y=0;        return;    }    if(val[p]<=v) x=p,split(rc,v,rc,y);    else y=p,split(lc,v,x,lc);    pushup(p);}int getmin(int p){    while(1){        if(lc) p=lc;        else return val[p];    }}void renode(int p){    lc=rc=0;}void dfs(int p,int &f){    if(lc) dfs(lc,f);    if(rc) dfs(rc,f);    int x,y;    split(f,val[p],x,y);    renode(p);    f=merge(merge(x,p),y);}void DFS(int p){    if(lc) DFS(lc);    cout<<pos[p]<<" ";    if(rc) DFS(rc);}void un(int x,int y){    int r[2];    r[0]=_find(x);    r[1]=_find(y);    if(r[0]==r[1]) return;    if(siz[r[0]]<siz[r[1]]) swap(r[0],r[1]);    dfs(r[1],r[0]);    lnk[_find(x)]=lnk[_find(y)]=r[0];    lnk[r[0]]=r[0];}int get_th(int p,int k){    while(1){        if(k<=siz[lc]) p=lc;        else if(k==siz[lc]+1) return p;        else k-=siz[lc],k--,p=rc;    }}void query(int a,int k){    int rt=_find(a);    if(siz[rt]<k) puts("-1");    else printf("%d\n",pos[get_th(rt,k)]);}int main(){    srand(19260817);    scanf("%d%d",&n,&m);    for(int i=1;i<=n;++i){        scanf("%d",&c[i]);        //ins(i,c[i]);        col[i]=newnode(i);    }    for(int i=1;i<=n;++i) lnk[i]=i;    int a,b;    while(m--) scanf("%d%d",&a,&b),un(a,b);    scanf("%d",&q);    int x,y,c,d;    char op[10];    while(q--){        scanf("%s",op);        scanf("%d%d",&x,&y);        if(op[0]=='B') un(x,y);        if(op[0]=='Q') query(x,y);    }    return 0;}