【HNOI2012】永无乡【线段树合并】【并查集】

5 篇文章 0 订阅
2 篇文章 0 订阅

传送门

其实很裸,用并查集维护每个点的信息已经别合并到哪个线段树里面了,然后权值线段树查第k大就行。

我一开始和naive地没用并查集,然后发现rt没有传递性。

写着写着反应过来,,其实一开始也可以这么写,,用rt当并查集用就行,,我感觉更naive了。

#include<bits/stdc++.h>
using namespace std;
#define in read()
int in{
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){
		ch=getchar();if(ch=='-')f=-1;
	}
	while(isdigit(ch)){
		cnt=cnt*10+ch-48;
		ch=getchar();
	}return cnt*f;
}
int n,m,Rank[100003],rt[100003];
struct node{
	int l,r,size;
}t[3000003];int tot;
void modify(int &u,int l,int r,int key){
	if(!u)u=++tot;
	int mid=(l+r)>>1;t[u].size+=1;if(l==r)return;
	if(key<=mid)modify(t[u].l,l,mid,key);
	else modify(t[u].r,mid+1,r,key);
}
int merge(int u,int v){
	if(!u||!v)return u+v;
	t[u].size+=t[v].size;
	t[u].l=merge(t[u].l,t[v].l);
	t[u].r=merge(t[u].r,t[v].r);
	return u;
}int belong[100003];
int query(int u,int l,int r,int k){
	if(l==r){
		return l;
	}
	int mid=(l+r)>>1,ls=t[t[u].l].size;
	if(k<=ls)return query(t[u].l,l,mid,k);
	else return query(t[u].r,mid+1,r,k-ls); 
}
void debug(int u,int l,int r){
	cout<<u<<" "<<l<<" "<<r<<" "<<t[u].size<<endl;int mid=(l+r)>>1;
	if(t[u].l)debug(t[u].l,l,mid);if(t[u].r)debug(t[u].r,mid+1,r);
}int fa[100003];
int find(int x){
	if(x==fa[x])return x;
	return fa[x]=find(fa[x]);
}
signed main(){
	n=in;m=in;tot=n;for(int i=1;i<=n;i++)Rank[i]=in,belong[Rank[i]]=i;
	for(int i=1;i<=n;i++)fa[i]=i;//belong[n+1]=-1;
	for(int i=1;i<=m;i++){
		int a=in;int b=in;int x=find(a),y=find(b);fa[x]=y;//rt[x]=rt[y];
	}int q=in;
	for(int i=1;i<=n;i++){
		modify(rt[find(i)],1,n,Rank[i]);
	}char ch[3];int x,y;
	while(q--){
		scanf("%s",ch);x=in;y=in;
		if(ch[0]=='B'){
			int a=find(x),b=find(y);
			if(rt[a]==rt[b])continue;//cout<<"X segment "<<endl;
			//debug(rt[x],1,n);cout<<"Y segment"<<endl;debug(rt[y],1,n);
			rt[a]=merge(rt[a],rt[b]);//rt[b]=rt[a];//cout<<"after segment "<<endl;
			//debug(rt[x],1,n);
			fa[b]=a;
		}else{
			x=find(x);
			if(y>t[rt[x]].size){cout<<"-1"<<'\n';continue;}
			int gu=query(rt[x],1,n,y);//cout<<"gu "<<gu<<" ";
			cout<<belong[gu]<<'\n';
		} 
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值