[BZOJ3545][ONTAK2010]Peaks(splay启发式合并)

题目描述

传送门

题解

离线之后排序,然后对于每一个询问,将所有边权比它小的加入。可以把各个连通块搞成一个个splay,连边的时候启发式合并,然后每一个询问在当前点所在的splay里找k大值就可以了。
这题还有主席树的做法,就是通过排序之后将点重新编号,保证在任何时刻和某一个点连通的所有点组成一个连续的区间,然后实际上就是一个静态主席树找区间第k大了。

代码

#include
#include
#include
#include
using namespace std;
#define N 100005
#define M 500005

int n,m,q;
int h[N],f[N],ch[N][2],size[N],key[N],root[N];
struct hp{int x,y,z,id,k;}e[M],a[M];
int dig[20],ans[M];

int read()
{
    int x=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
void write(int x)
{
    dig[0]=0;
    if (!x) {puts("0");return;}
    if (x==-1) {puts("-1");return;}
    while (x)
    {
        dig[++dig[0]]=x%10;
        x/=10;
    }
    for (int i=dig[0];i>=1;--i) putchar(dig[i]+'0');
    putchar('\n');
}
int cmp(hp a,hp b)
{
    return a.znamespace UFS
{
    int f[N];
    void clear()
    {
        for (int i=1;i<=n;++i)
            f[i]=i;
    }
    int find(int x)
    {
        if (x==f[x]) return x;
        f[x]=find(f[x]);
        return f[x];
    }
}

int get(int x)
{
    return ch[f[x]][1]==x;
}
void update(int x)
{
    size[x]=1;
    if (ch[x][0]) size[x]+=size[ch[x][0]];
    if (ch[x][1]) size[x]+=size[ch[x][1]];
}
void rotate(int x)
{
    int old=f[x],oldf=f[old],wh=get(x);
    if (oldf) ch[oldf][ch[oldf][1]==old]=x;
    f[x]=oldf;
    ch[old][wh]=ch[x][wh^1];
    if (ch[old][wh]) f[ch[old][wh]]=old;
    ch[x][wh^1]=old;
    f[old]=x;
    update(old);
    update(x);
}
void splay(int rt,int x)
{
    for (int fa;fa=f[x];rotate(x))
        if (f[fa])
            rotate( (get(x)==get(fa))?fa:x );
    root[rt]=x;
}
void insert(int rt,int x)
{
    int now=root[rt],fa=0;
    while (1)
    {
        fa=now;
        now=ch[now][key[x]>key[now]];
        if (!now)
        {
            f[x]=fa;ch[fa][key[x]>key[fa]]=x;
            size[x]=1;
            update(fa);
            splay(rt,x);
            break;
        }
    }
}
int find(int rt,int x)
{
    int now=root[rt];
    while (1)
    {
        if (x<=size[ch[now][0]]) now=ch[now][0];
        else
        {
            x-=size[ch[now][0]];
            if (x==1)
            {
                splay(rt,now);
                return now;
            }
            --x;
            now=ch[now][1];
        }
    }
}
void merge(int x,int rt)
{
    int lch=ch[x][0],rch=ch[x][1];
    ch[x][0]=ch[x][1]=f[x]=size[x]=0;
    insert(rt,x);
    if (lch) merge(lch,rt);
    if (rch) merge(rch,rt);
}

int main()
{
    n=read();m=read();q=read();
    for (int i=1;i<=n;++i)
        h[i]=read();
    for (int i=1;i<=m;++i)
        e[i].x=read(),e[i].y=read(),e[i].z=read();
    sort(e+1,e+m+1,cmp);
    for (int i=1;i<=q;++i)
        a[i].x=read(),a[i].z=read(),a[i].k=read(),a[i].id=i;
    sort(a+1,a+q+1,cmp);
    for (int i=1;i<=n;++i) root[i]=i,size[i]=1,key[i]=h[i];

    UFS::clear();int now=1;
    for (int i=1;i<=q;++i)
    {
        while (now<=m&&e[now].z<=a[i].z)
        {
            int f1=UFS::find(e[now].x),f2=UFS::find(e[now].y);
            if (f1!=f2)
            {
                if (size[root[f1]]>size[root[f2]]) swap(f1,f2);
                UFS::f[f1]=f2;
                merge(root[f1],f2);root[f1]=0;
            }
            now++;
        }
        int rt=UFS::find(a[i].x);
        if (size[root[rt]]1;
        else ans[a[i].id]=key[find(rt,size[root[rt]]-a[i].k+1)];
    }
    for (int i=1;i<=q;++i)
        write(ans[i]);
}

你可能感兴趣的:(题解,平衡树,启发式合并)