【bzoj2733】【HNOI2012】永无乡

来源:互联网 发布:java中常用io流类 编辑:程序博客网 时间:2024/05/17 20:21

题意

  给定一幅图,图上有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)/2using 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;}