bzoj 2733 平衡树启发式合并

  首先对于一个连通块中,询问我们可以直接用平衡树来求出排名,那么我们可以用并查集来维护各个块中的连通情况,对于合并两个平衡树,我们可以暴力的将size小的平衡树中的所有节点删掉,然后加入大的平衡树中,因为每个点只可能被删除插入logn次,所以时间复杂度为nlog^2n。

  

/**************************************************************
    Problem: 2733
    User: BLADEVIL
    Language: C++
    Result: Accepted
    Time:2112 ms
    Memory:64868 kb
****************************************************************/
 
//By BLADEVIL
#include <cstdio>
#define maxn 100010
#define maxt 4000010
 
using namespace std;
 
int n,m;
int father[maxn],a[maxn],root[maxn];
int left[maxt],right[maxt],size[maxt],key[maxt];
int tot,save;
int adr[maxn];
 
int getfather(int x)
{
    if (father[x]==x) return x;
    return father[x]=getfather(father[x]);
}
 
void swap(int &x,int &y)
{
    int z=x; 
    x=y; y=z;
}
 
void left_rotate(int &t)
{
    int k=right[t];
    right[t]=left[k];
    left[k]=t;
    size[k]=size[t];
    size[t]=size[left[t]]+size[right[t]]+1;
    t=k;
}
 
void right_rotate(int &t)
{
    int k=left[t];
    left[t]=right[k];
    right[k]=t;
    size[k]=size[t];
    size[t]=size[left[t]]+size[right[t]]+1;
    t=k;
}
 
void maintain(int &t,bool flag)
{
    if (!flag)
    {
        if (size[left[left[t]]]>size[right[t]])
            right_rotate(t); else
        if (size[right[left[t]]]>size[right[t]])
            left_rotate(left[t]),right_rotate(t); else return;
    } else
    {
        if (size[right[right[t]]]>size[left[t]])
            left_rotate(t); else
        if (size[left[right[t]]]>size[left[t]])
            right_rotate(right[t]),left_rotate(t); else return;
    }
    maintain(left[t],0); maintain(right[t],1);
    maintain(t,1); maintain(t,0);
}
 
void t_insert(int &t,int v)
{
    if (!t)
    {
        t=++tot;
        left[t]=right[t]=0;
        key[t]=v;
        size[t]=1;
    } else
    {
        size[t]++;
        if (v>key[t]) t_insert(right[t],v); else t_insert(left[t],v);
        maintain(t,v>key[t]);
    }
}
 
int t_delete(int &t,int v)
{
    size[t]--;
    if ((key[t]==v)||((v>key[t])&&(!right[t]))||((v<key[t])&&(!left[t])))
    {   
        save=key[t];
        if ((!left[t])||(!right[t]))
            t=left[t]+right[t]; else key[t]=t_delete(left[t],v+1);
     
    } else
        return (v>key[t])?t_delete(right[t],v):t_delete(left[t],v);
}
 
int t_rank(int &t,int k)
{
    if (size[left[t]]+1==k) return key[t];
    return (k<=size[left[t]])?t_rank(left[t],k):t_rank(right[t],k-size[left[t]]-1);
}
 
void combine(int x,int y)
{
    int fx,fy;
    fx=getfather(x); fy=getfather(y);
    if (size[root[fx]]<size[root[fy]]) swap(fx,fy);
    father[fy]=fx;
    if (fx!=fy)
    {
        fy=root[fy];
        while (root[fy])
        {
            t_insert(root[fx],a[fy]);
            t_delete(root[fy],a[fy]);
        }
    }
}
 
void ask(int x,int k)
{
    x=root[getfather(x)];
    if (size[x]>=k) printf("%d\n",adr[t_rank(x,k)]); else printf("-1\n");
}
 
void init()
{
    int x,y;
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<=n;i++) adr[a[i]]=i;
    for (int i=1;i<=n;i++) father[i]=i,t_insert(root[i],a[i]);
    while (m--)
    {
        scanf("%d%d",&x,&y);
        combine(x,y);
    }
}
 
void solve()
{
    int test,x,y; 
    char s[10];
    scanf("%d",&test);
    while (test--)
    {
        scanf("%s%d%d",&s,&x,&y);
        if (s[0]=='B')
            combine(x,y); else ask(x,y);
    }
}
 
int main()
{
    init();
    solve();
    return 0;
}

 

posted on 2014-03-26 15:15  BLADEVIL  阅读(1147)  评论(0编辑  收藏  举报