LG-P3244-永无乡

永无乡

题目大意

给你n个点,每个点有权值k,现有两种操作:将两个点所在联通块合并,查询某个点所在联通块权值第k小是哪个数

思路

一看就知道是线段树合并,但是写不来。

用并查集来维护联通块,合并的同时再合并线段树即可,注意是权值线段树,并且输出的是点的编号不是值。

$Code$
#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)

const int N=1e5+5,M=3e5+5;
int val[N],fa[N],rt[N],lsh[N];
int ls[N*20],rs[N*20],sum[N*20],co;
int n,m,t;

int fi(int x){
	if(fa[x]==x)	return x;
	return fa[x]=fi(fa[x]);
}
void mer(int x,int y){
	fa[fi(x)]=fi(y);
}

//线段树
void pushup(int node){
	sum[node]=sum[ls[node]]+sum[rs[node]];
}
int tr_mer(int x,int y,int l,int r){
	if(!x)	return y;
	if(!y)	return x;
	if(l==r){
		sum[x]+=sum[y];
		return x;
	}
	ls[x]=tr_mer(ls[x],ls[y],l,mid);
	rs[x]=tr_mer(rs[x],rs[y],mid+1,r);
	pushup(x);
	return x;
} 
int change(int node,int l,int r,int pos,int x){
	if(!node)	node=++co;
	if(l==r){
		sum[node]+=x;
		return node;
	}
	if(pos<=mid)	ls[node]=change(ls[node],l,mid,pos,x);
	else	rs[node]=change(rs[node],mid+1,r,pos,x);
	pushup(node);
	return node;
}
int ask(int node,int l,int r,int k){
	if(!node || sum[node]<k)	return -1;
	if(l==r)	return lsh[l];
	if(sum[ls[node]]>=k)	return ask(ls[node],l,mid,k);
	else	return ask(rs[node],mid+1,r,k-sum[ls[node]]);
}

int main(){
	
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
		scanf("%d",&val[i]),lsh[val[i]]=i,rt[i]=change(0,1,n,val[i],1),fa[i]=i;
	for(int i=1;i<=m;++i){
		int x,y;
		scanf("%d%d",&x,&y);
		x=fi(x),y=fi(y);
		mer(x,y);
		rt[fi(x)]=tr_mer(rt[x],rt[y],1,n);
	}
	scanf("%d",&t);
	for(int i=1;i<=t;++i){
		char op;int x,y;
		cin>>op;scanf("%d%d",&x,&y);
		if(op=='Q')
			printf("%d\n",ask(rt[fi(x)],1,n,y));
		else{
			x=fi(x),y=fi(y);
			mer(x,y);
			rt[fi(x)]=tr_mer(rt[x],rt[y],1,n);
		}
	}
	
	return 0;
}

\({\color{skyblue}{谨此纪念我过的第一道线段树合并的题}}\)

posted @ 2022-04-30 16:26  _yolanda  阅读(34)  评论(0编辑  收藏  举报