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

题目:

我是超链接

题解:

求解集合内第k大的话是splay的基本操作
那么维护连通性用并查集,然后就是个splay启发式合并的裸题了。
splay启发式合并,按照splay的后序遍历进行合并,不然如果你从头就开始合并父子关系就混乱了
时间复杂度是 O(nlog2n) O ( n l o g 2 n )

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N=100005;
int ch[N][2],f[N],fa[N],size[N],root[N],key[N];
int findfa(int x){if (fa[x]!=x) fa[x]=findfa(fa[x]);return fa[x];}
int get(int x){return ch[f[x]][1]==x;}
void updata(int now)
{
    size[now]=1;
    if (ch[now][0]) size[now]+=size[ch[now][0]];
    if (ch[now][1]) size[now]+=size[ch[now][1]];
}
void rotate(int x)
{
    int old=f[x],oldf=f[old],which=get(x);
    f[x]=oldf; if (oldf) ch[oldf][get(old)]=x;
    ch[old][which]=ch[x][which^1]; f[ch[x][which^1]]=old;
    ch[x][which^1]=old; f[old]=x;
    updata(old); 
    updata(x);
}
void splay(int x,int id)
{
    for (;f[x];rotate(x))
      if (f[f[x]]) rotate(get(x)==get(f[x])?f[x]:x);
    root[id]=x;
}
int find(int x,int now)//找排名为x的编号 
{
    while (1)
    {
        if (ch[now][0] && size[ch[now][0]]>=x) now=ch[now][0];
        else 
        {
            x-=size[ch[now][0]]+1;
            if (x<=0) return now;
            now=ch[now][1];
        }
    }
}
void insert(int sz,int x,int to)
{
    int now=root[to],fa=0;
    while (1)
    {
         fa=now; now=ch[now][key[now]<x];
         if (!now)
         {
            f[sz]=fa; ch[fa][key[fa]<x]=sz;
            size[sz]=1; 
            updata(fa);
             splay(sz,to);return;
         }
    }
}
void merge(int now,int to)
{
    if (ch[now][0]) merge(ch[now][0],to);
    if (ch[now][1]) merge(ch[now][1],to);
    insert(now,key[now],to);
}
//key f ch[0] ch[1] size
int main()
{
    int n,m,q;scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&key[i]),fa[i]=root[i]=i,size[i]=1;
    for (int i=1;i<=m;i++)
    {
        int x,y;scanf("%d%d",&x,&y);
        int xa,ya;xa=findfa(x);ya=findfa(y);
        if (xa==ya) continue;
        if (size[root[xa]]>size[root[ya]]) swap(x,y),swap(xa,ya);
        merge(root[xa],ya); fa[xa]=ya; 
    }
    scanf("%d",&q);
    while (q--)
    {
        char st[5]; int x,y;scanf("%s",st);
        scanf("%d%d",&x,&y);
        if (st[0]=='Q')
        {
            x=findfa(x);
            if (y>size[root[x]]) printf("-1\n");
            else printf("%d\n",find(y,root[x]));
        }else
        {
            int xa,ya;xa=findfa(x);ya=findfa(y);
            if (xa==ya) continue;
            if (size[root[xa]]>size[root[ya]]) swap(x,y),swap(xa,ya);
            merge(root[xa],ya); fa[xa]=ya;
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值