[bzoj2733][HNOI2012]永无乡

来自FallDream的博客,未经允许,请勿转载,谢谢。


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

这道题很容易看出来是平衡树+启发式合并  

就是每一个联通块都维护一棵平衡树,然后建桥的时候如果两个点不在同一颗树,那么就把它们所在的平衡树合并。合并的时候选择把size较小的平衡树的点全部拿出来,插入到另一棵里面,可以证明这样的复杂度是最坏$O(nlog^{2}n)$的。

#include<iostream>
#include<cstdio>
#define MN 100000
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int fa[MN+5],bel[MN+5],size[MN+5],c[MN+5][2],n,m,rt[MN+5],top,q[MN+5],s[MN+5];
inline void update(int x){size[x]=size[c[x][0]]+size[c[x][1]]+1;}

void rotate(int x,int&k)
{
    int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
    if(y==k) k=x; else c[z][c[z][1]==y]=x;
    fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
    c[y][l]=c[x][r];c[x][r]=y;
    update(y);update(x);
}

void splay(int x,int&k)
{
    for(;x!=k;rotate(x,k))
        if(fa[x]!=k) rotate((c[fa[fa[x]]][1]==fa[x]^c[fa[x]][1]==x)?x:fa[x],k);
}

void ins(int&x,int k,int last)
{
    if(!x){x=k;fa[x]=last;size[x]=1;c[x][0]=c[x][1]=0;return;}
    ins(c[x][s[k]>s[x]],k,x);++size[x];
}

void dfs(int x)
{
    if(c[x][0]) dfs(c[x][0]);
    q[++top]=x;
    if(c[x][1]) dfs(c[x][1]);
}

void Merge(int x,int y)
{
    splay(x,rt[bel[x]]);splay(y,rt[bel[y]]);
    if(size[x]>size[y]) swap(x,y);
    top=0;dfs(x);
    for(int i=1;i<=top;i++) bel[q[i]]=bel[y],ins(rt[bel[y]],q[i],0),splay(q[i],rt[bel[y]]);
}

int find(int x,int rk)
{
    int sz=size[c[x][0]]+1;
    if(sz==rk) return x;
    if(sz>rk) return find(c[x][0],rk);
    return find(c[x][1],rk-sz);
}

char op[5];
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)bel[i]=rt[i]=i,size[i]=1;
    for(int i=1;i<=n;i++)s[i]=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        if(bel[x]!=bel[y]) Merge(x,y);
    }
    m=read();
    for(int i=1;i<=m;i++)
    {
        scanf("%s",op+1);int x=read(),y=read();
        if(op[1]=='Q') printf("%d
",size[rt[bel[x]]]<y?-1:find(rt[bel[x]],y));
        else if(bel[x]!=bel[y]) Merge(x,y);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/FallDream/p/bzoj2733.html