【bzoj2733】【HNOI2012】永无乡

题意

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

解法

线段树(主席树)+启发式合并:
  这道题和 bzoj3545Peaks 类似,只是没有边权,然后是求第 k
  首先,我们考虑用并查集来维护是否联通,这很容易就能做到
  其次,求第k小的数可以用主席树来进行,我们对于每一个点建立一棵值域线段树,范围是 1n ,因为所有的值域都相同,因此可以使用启发式合并
  对于连接操作,首先判断 u v是否在同一个联通块内,如果不在,我们就需要合并 u v所在的联通块的值域线段树,然后将 u v加入同一个联通块内(这里可以使用并查集的按秩合并,这样会快一点)
  在询问时,因为我们询问出的是一个权值,即重要度排名,而题目要求的是点的编号,所以我们要用一个 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值