【bzoj2733】【HNOI2012】永无乡

题意

  给定一幅图,图上有n个节点,一开始有m条边
  每个节点有一个重要度排名:WiWi1n
  现在有两种操作:
  ①.连接uv
  ②.询问与v联通的点集中,重要度第k小的点的编号
  非强制在线

解法

线段树(主席树)+启发式合并:
  这道题和bzoj3545Peaks类似,只是没有边权,然后是求第k
  首先,我们考虑用并查集来维护是否联通,这很容易就能做到
  其次,求第k小的数可以用主席树来进行,我们对于每一个点建立一棵值域线段树,范围是1n,因为所有的值域都相同,因此可以使用启发式合并
  对于连接操作,首先判断uv是否在同一个联通块内,如果不在,我们就需要合并uv所在的联通块的值域线段树,然后将uv加入同一个联通块内(这里可以使用并查集的按秩合并,这样会快一点)
  在询问时,因为我们询问出的是一个权值,即重要度排名,而题目要求的是点的编号,所以我们要用一个rank数组来记录每一个重要度排名对应的点的编号

复杂度

Onlog2n

代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
#define mid (l+r)/2
using namespace std;
const int MAXN=100010;
struct node
{
    int u,v;
    void gi()   { scanf("%d%d",&u,&v); }
}t[MAXN];
int head[MAXN],num;
int ls[MAXN*20],rs[MAXN*20];
int rt[MAXN],T[MAXN*20];
int w[MAXN],f[MAXN];
int rk[MAXN];
int n,m,q,cnt;
int find(int x)
{
    if( x!=f[x] )   f[x]=find( f[x] );
    return f[x];
}
void insert(int &k,int l,int r,int x)
{
    if( !k )   k=++cnt;
    if( l==r )   { T[k]=1;return ; }
    if( x<=mid )   insert( ls[k],l,mid,x );
    else   insert( rs[k],mid+1,r,x );
    T[k]=T[ls[k]]+T[rs[k]];
}
int query(int k,int l,int r,int x)
{
    if( l==r )   return l;
    if( T[ls[k]]>=x )   return query( ls[k],l,mid,x );
    else   return query( rs[k],mid+1,r,x-T[ls[k]] );
}
int merge(int x,int y)
{
    if( !x || !y )   return x+y;
    ls[x]=merge( ls[x],ls[y] );
    rs[x]=merge( rs[x],rs[y] );
    T[x]=T[ls[x]]+T[rs[x]];
    return x;
}
void Union(int u,int v)
{
    u=find( u ),v=find( v );
    if( u==v )   return ;
    rt[u]=merge( rt[u],rt[v] );
    f[v]=u;
}
int main()
{
    char s[30];
    int tmp,u,v;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&w[i]);
        f[i]=i,rk[w[i]]=i;
    }
    for(int i=1;i<=n;i++)   insert( rt[i],1,n,w[i] );
    for(int i=1;i<=m;i++)   scanf("%d%d",&u,&v),Union( u,v );
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%s",s);
        scanf("%d%d",&u,&v);
        if( s[0]=='B' )   Union( u,v );
        else
        {
            tmp=rt[find( u )];
            if( T[tmp]<v )   printf("%d\n",-1);
            else   printf("%d\n",rk[query( tmp,1,n,v )]);
        }
    }
    return 0;
}
posted @ 2017-10-02 16:44  清疚  阅读(65)  评论(0)    收藏  举报