bzoj2733: [HNOI2012]永无乡 splay+启发式合并

正解不是splay,但标解是splay+启发式合并。所谓的启发式合并就是把size小的往大的splay上加,复杂度是nlog2n。

由于无需知道根所以直接看此节点有没有父亲节点就可以判断是不是根。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
#define maxn 120110
int fa[maxn],tr[maxn][2],val[maxn],lab[maxn],f[maxn],siz[maxn],cnt;
int n,m,q[maxn];
int getfa(int x){return x == f[x] ? x : f[x] = getfa(f[x]);}
void pushup(int rt)
{
    if (!rt) return;
    int ls=tr[rt][0],rs=tr[rt][1];
    siz[rt]=siz[ls]+siz[rs]+1;
}
void rotate(int x)
{
    int l,r,y,z;
    y=fa[x];z=fa[y];
    if(tr[y][0]==x) l=0;
    else l=1;r=l^1;
    if(fa[y])
    {
        if(tr[z][0]==y) tr[z][0]=x;
        else tr[z][1]=x;
    }
    fa[x]=z;fa[y]=x;fa[tr[x][r]]=y;
    tr[y][l]=tr[x][r];tr[x][r]=y;
    pushup(y),pushup(x);
}
void splay(int x)
{
    int z,y;
    while(fa[x])
    {
        y=fa[x];z=fa[y];
        if(fa[y])
        {
            if((tr[y][0]==x)^(tr[z][0]==y)) rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
}
int select(int x,int rk)
{
    int l=tr[x][0],r=tr[x][1];
    int rak=siz[l]+1;
    if(rak==rk) return x;
    if(rak>rk) return select(l,rk);
    else return select(r,rk-rak);
}
void insert(int &t,int anc,int now)
{
    if (t == 0)
    {
        t = now;
        fa[t] = anc;
        siz[t] = 1;
        splay(t);
        return;
    }
    if (val[now] <= val[t]) insert(tr[t][0],t,now);
    else insert(tr[t][1],t,now);
    pushup(t);
}
int query(int a,int k)
{
    if(siz[a]<k) return -1;
    else return select(a,k);
}
void update(int a,int b)
{
    splay(a),splay(b);
    if(siz[a]>siz[b]) swap(a,b);
    //printf("%d %d\n",siz[root[getfa(a)]],siz[root[getfa(b)]]);
    int head = 0,tail = 1;
    q[0] = b;q[1] = a;
    while (head < tail)
    {
        int a = q[++ head];
        if (tr[a][0]) q[++ tail] = tr[a][0];
        if (tr[a][1]) q[++ tail] = tr[a][1];
        tr[a][0] = tr[a][1] = 0;
        insert(q[head - 1],0,a);
    }
}
int main()
{
    int a,b;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&val[i]);
        f[i]=i;siz[i]=1;
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        if(getfa(a)!=getfa(b))
        {
            update(a,b);
            f[f[a]]=f[b];
        }
 
    }
    int op;char st[10];
    scanf("%d",&op);
    while(op--)
    {
        scanf("%s%d%d",st,&a,&b);
        if(st[0]=='Q')
        {
            splay(a);
            printf("%d\n",query(a,b));
        }
        else
        {
            if(getfa(a)!=getfa(b))
            {
                update(a,b);f[f[a]]=f[b];
            }
        }
    }
    return 0;
}
/*
5 1
4 3 2 5 1
1 2
7
Q 3 2
Q 2 1
B 2 3
B 1 5
Q 2 1
Q 2 4
Q 2 3
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值