[HNOI2012]永无乡(线段树合并)

在每个点建立值域线段树,连边时用并查集判断连通性,然后将两个连通块的祖先合并,最后查询时输出x所在连通块的祖先的信息就行了

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+7;
int fa[N],Rank[N],Island[N];
int Find(int x)
{
	if(x==fa[x]) return x;
	return fa[x]=Find(fa[x]);
}
int n,m,q;
inline int read()
{
	int X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}
int cnt[10*N],lson[10*N],rson[10*N],tot,rot[N];
void pushup(int k)
{
	cnt[k]=cnt[lson[k]]+cnt[rson[k]];
}
void update(int &k,int l,int r,int x)
{
	k=++tot;
	if(l==r)
	{
		cnt[k]=1;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) update(lson[k],l,mid,x);
	else update(rson[k],mid+1,r,x);
	pushup(k);
}
void merge(int x,int y,int l,int r)
{
	if(l==r) return;
	int mid=(l+r)>>1;
	if(lson[x]&&lson[y]) merge(lson[x],lson[y],l,mid);
	else if(!lson[x]&&lson[y]) lson[x]=lson[y];
	if(rson[x]&&rson[y]) merge(rson[x],rson[y],mid+1,r);
	else if(!rson[x]&&rson[y]) rson[x]=rson[y];
	pushup(x);
}
int query(int x,int l,int r,int k)
{	
	if(cnt[x]<k) return -1;
	if(l==r) return l;
	int ans=cnt[lson[x]];
	int mid=(l+r)>>1;
	if(ans>=k) return query(lson[x],l,mid,k);
	else return query(rson[x],mid+1,r,k-ans);
}
int main()
{
	n=read();
	m=read();
	for(int i=1;i<=n;i++)
	{
		Rank[i]=read();
		Island[Rank[i]]=i;
		update(rot[i],1,n,Rank[i]);
		fa[i]=i;
	}
	for(int i=1;i<=m;i++)
	{
		int x,y;
		x=read();
		y=read();
		int fx=Find(x),fy=Find(y);
		if(fx==fy) continue;
		fa[fy]=fx;
		merge(rot[fx],rot[fy],1,n);
	}
	char opt[3];
	q=read();
	while(q--)
	{
		int x,y;
		scanf("%s%d%d",opt,&x,&y);
		if(opt[0]=='B')
		{
			int fx=Find(x);
			int fy=Find(y);
			if(fx==fy) continue;
			fa[fy]=fx;
			merge(rot[fx],rot[fy],1,n);
		}
		else
		{
			int root=Find(x);
			int ans=query(rot[root],1,n,y);
			if(ans==-1) printf("-1\n");
			printf("%d\n",Island[ans]);
		}
	}
	return 0;
}







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值