BZOJ-2733: [HNOI2012]永无乡

[文章目录]

Description

就是给你n个数的排名先后,各自为一个集合。然后给出q次操作,每次合并两个集合+查询某一集合排名第k的数的id。

启发式合并splay_tree,每次将小的拆了扔到大的里面。考虑每个节点的合并次数。只有当这个节点在较小的集合中才会被合并。每次所在集合增大至少二倍,所以最多被插入logn次。加上每次插入logn,时间复杂度为O(n*n)。不过据说由于splay因为每次插入都将其旋转到根,大概每次合并的时间复杂度就是siz的,启发式合并splay的时间复杂度就变成了O(nlogn)的。

不过我写的splay。。。一直常数巨大。status里没几个比我慢的。
时间复杂度什么的。。。并没有什么用的。

指针版码力要求感人。

重构三遍得出的经验:
round 1:直接把x所在指针当做根,RE
round 2:每次从x向上一直找到根,中间所有的root都需要重构,细节多,RE
round 3:没有哨兵节点,直接搞,需要特判是否有开区间端点的各种情况,RE
round 4:用一个全局变量now记录当前所在的tree,在rotate的时候直接改变root,使之始终指向根,外加并查集维护splay的根,1A

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,q,fa[101000],now;
char st[200];
int read()
{
    char ch=getchar(); int re=0;
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9')
        re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
    return re;
}
int find(int x){return (x==fa[x])?x:fa[x]=find(fa[x]);}
struct node
{
    node *c[2],*fa;
    int siz,w,id;
    node(int x,int y);
    void pushup(){siz=c[0]->siz+c[1]->siz+1;}
}*null=new node(0,0),*root[101000];
void initialize()
{
    for(int i=1;i<=n;i++)
    {
        fa[i]=i;
        root[i]->c[0]=new node(-1<<30,0);
        root[i]->c[1]=new node(1<<30,0);
        root[i]->c[0]->fa=root[i];
        root[i]->c[1]->fa=root[i];
        root[i]->pushup();
    }
}
node::node(int x,int y)
{
    fa=c[0]=c[1]=null;  
    siz=null?1:0;
    w=x; id=y;
}
void rotate(node *x)
{
    node *y=x->fa; bool l=(x==y->c[1]),r=l^1;
    y->c[l]=x->c[r]; x->c[r]->fa=y;
    x->c[r]=y; x->fa=y->fa;
    if(y==y->fa->c[0]) y->fa->c[0]=x;
    else y->fa->c[1]=x; 
    y->fa=x; y->pushup(); x->pushup();
    if(root[now]==y) root[now]=x;
}
void splay(node *x,node *tar)
{
    while(x->fa!=tar)
    {
        node *y=x->fa,*z=y->fa;
        if(z==tar){rotate(x); break;}
        if((x==y->c[0])^(y==z->c[0])==0) rotate(y);
        else rotate(x); rotate(x);
    }
}
void insert(node *x)
{
    if(x->w==1<<30||x->w==-1<<30) return ;
    node *p=root[now],*l,*r; int y=x->w;
    while(p!=null)
        if(y<p->w) r=p,p=p->c[0];
        else l=p,p=p->c[1];
    splay(l,null); splay(r,root[now]);
    x->siz=1; x->c[0]=x->c[1]=null;
    r->c[0]=x; x->fa=r;
    r->pushup(); l->pushup();
}
void merge(node *x)
{
    if(x==null) return ;
    merge(x->c[0]);
    node *p=x->c[1];
    insert(x);
    merge(p);
}
void query(int x,int y)
{
    node *p=root[find(x)];
    if(y>p->siz-2) puts("-1");
    else
    {
        y++;
        while(1)
        {
            if(y<=p->c[0]->siz) p=p->c[0];
            else
            {
                y-=p->c[0]->siz+1;
                if(!y) {printf("%d\n",p->id); return ;}
                p=p->c[1];
            }
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) root[i]=new node(read(),i);
    initialize();
    int x,y;
    while(m--)
    {
        scanf("%d%d",&x,&y);
        int dx=find(x),dy=find(y);
        if(dx!=dy)
        {
            if(root[dx]->siz>root[dy]->siz) swap(dx,dy);
            now=dy;
            merge(root[dx]); fa[dx]=dy;
        }
    }
    scanf("%d",&q);
    while(q--)
    {
        scanf("%s%d%d",st,&x,&y);
        if(st[0]=='B')
        {
            int dx=find(x),dy=find(y);
            if(dx!=dy)
            {
                if(root[dx]->siz>root[dy]->siz) swap(dx,dy);
                now=dy;
                merge(root[dx]); fa[dx]=dy;
            }
        }
        else query(x,y);
    }
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注