[HNOI2012]永无乡「线段树合并」

题目描述

这不是个链接

思路分析

  • 这题线段树合并巨好写啊(我是不会告诉你其实是因为我不会写平衡树的)。然后我也没怎么卡常就跑到了洛谷第一页,平衡树脸面何在
  • 排名的值域很小,所以直接对每个节点开一棵关于排名的权值线段树,线段树的下标就是排名,往里面塞个数就行了,这样查询 \(k\) 小也很简单,因为这时候线段树存储的是排名位于一段区间内的点的个数,直接找个数为 \(k\) 的那个位置就好了。奥对了,每个叶子节点需要记录一下塞到这个节点的编号。
  • 维护联通性时用并查集简单处理就好了,并查集和线段树同时同向合并,然后好像就没了,成功变成了线段树合并裸题

\(Code\)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define R register
#define N 100010
using namespace std;
inline int read(){
	int x = 0,f = 1;
	char ch = getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m,q,fa[N],id[N<<5],tr[N<<5],ls[N<<5],rs[N<<5],root[N<<5],cnt;
char op[10];
int find(int x){
	return fa[x]==x ? x : (fa[x]=find(fa[x]));
}
void modify(int &rt,int l,int r,int pos,int idx){//pos将排名转化为下标
	if(!rt)rt = ++cnt;
	if(l==r){
		id[rt] = idx,tr[rt]++;//id记录编号
		return;
	}
	int mid = (l+r)>>1;
	if(pos<=mid)modify(ls[rt],l,mid,pos,idx);
	else modify(rs[rt],mid+1,r,pos,idx);
	tr[rt] = tr[ls[rt]] + tr[rs[rt]];
}
int merge(int a,int b,int l,int r){//线段树合并板子
	if(!a)return b;
	if(!b)return a;
	if(l==r){
		tr[a] += tr[b];
		return a;
	}
	int mid = (l+r)>>1;
	ls[a] = merge(ls[a],ls[b],l,mid);
	rs[a] = merge(rs[a],rs[b],mid+1,r);
	tr[a] = tr[ls[a]] + tr[rs[a]];
	return a;
}
int query(int rt,int l,int r,int k){//查k小相当于查线段树值刚好等于k的下标(对应的编号)
	if(tr[rt]<k||!rt)return 0;
	if(l==r)return id[rt];
	int mid = (l+r)>>1;
	if(k<=tr[ls[rt]])return query(ls[rt],l,mid,k);
	else return query(rs[rt],mid+1,r,k-tr[ls[rt]]);
}
int main(){
	n = read(),m = read();
	for(R int i = 1;i <= n;i++){
		fa[i] = i;
		int x = read();
		modify(root[i],1,n,x,i);
	}
	for(R int i = 1;i <= m;i++){
		int x = read(),y = read();
		x = find(x),y = find(y);
		fa[y] = x;
		root[x] = merge(root[x],root[y],1,n);
	}
	q = read();
	for(R int i = 1;i <= q;i++){
		scanf("%s",op);
		if(op[0]=='B'){
			int x = read(),y = read();
			x = find(x),y = find(y);
			if(x==y)continue;
			fa[y] = x;
			root[x] = merge(root[x],root[y],1,n);
		}else{
			int x = read(),y = read();
			x = find(x);
			int ans = query(root[x],1,n,y);
			if(!ans){
				puts("-1");
				continue;
			}
			else printf("%d\n",ans);
		}
	}
	return 0;
}
posted @ 2020-10-22 18:08  HH_Halo  阅读(121)  评论(0编辑  收藏  举报