1. 程式人生 > 實用技巧 >[HNOI2012]永無鄉「線段樹合併」

[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;
}