bzoj 2733 [HNOI2012]永无乡

时间:2022-05-03 08:55:24

线段树合并+并查集

在每一个联通块上建一个权值线段树

每次用并查集判断维护,线段树合并即可

相当于每一次询问都在这个联通块一个线段树上问区间第k小

 

#include <bits/stdc++.h>
using namespace std;
const int MAXN=2*1e5+100;
int n,m,q,rk[MAXN],fa[MAXN],root[MAXN];
int t,f[MAXN];
struct node
{
    int lc,rc,sum;
}sh[MAXN*15];
int getfather(int x)//并查集
{
    if (fa[x]==x)
      return x;
    fa[x]=getfather(fa[x]);
    return fa[x];
}
void insert(int &x,int l,int r,int where)
{
    if (!x)
    {
        t++;
        x=t;
    }
    if (l==r)
    {
        sh[x].sum=1;
        return;
    }
    int mid;
    mid=(l+r)/2;
    if (where<=mid)
      insert(sh[x].lc,l,mid,where);
    else
      insert(sh[x].rc,mid+1,r,where);
    sh[x].sum=sh[sh[x].lc].sum+sh[sh[x].rc].sum;
}
int merge(int u,int v)//线段树合并
{
    if (!u)
      return v;
    if (!v)
      return u;
    sh[u].lc=merge(sh[u].lc,sh[v].lc);
    sh[u].rc=merge(sh[u].rc,sh[v].rc);
    sh[u].sum=sh[sh[u].lc].sum+sh[sh[u].rc].sum;
    return u;
}
int query(int x,int l,int r,int k)
{
    if (l==r)
      return l;
    int mid;
    mid=(l+r)/2;
    if (sh[sh[x].lc].sum>=k)
      return query(sh[x].lc,l,mid,k);
    else
      return query(sh[x].rc,mid+1,r,k-sh[sh[x].lc].sum);
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&rk[i]);
        f[rk[i]]=i;
        fa[i]=i;
    }
    for (int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        if (getfather(u)!=getfather(v))
        {
            fa[getfather(u)]=getfather(v);
        }
    }
    for (int i=1;i<=n;i++)
    {
        insert(root[getfather(i)],1,n,rk[i]);//联通块上建线段树
    }
    scanf("%d",&q);
    for (int i=1;i<=q;i++)
    {
        int a,b;
        char ch[10];
        scanf("%s%d%d",ch,&a,&b);
        if (ch[0]=='Q')
        {
            int father;
            father=getfather(a);
            if (sh[root[father]].sum<b)
            {
                printf("-1\n");
            }
            else
            {
                printf("%d\n",f[query(root[father],1,n,b)]);//找第k小数
            }
        }
        else
        {
            if (getfather(a)!=getfather(b))
            {
                root[getfather(a)]=merge(root[getfather(b)],root[getfather(a)]);
                fa[getfather(a)]=getfather(b);//并查集维护
            }
        }
    }
}