【洛谷P3224】【HNOI2012】—永无乡(线段树合并)

传送门

线段树合并板子题

线段树合并的复杂度要根据两颗树的相似度而定的

又因为一颗线段树的值域确定了,其形状也就确定了(只要你是好好写的ll,mid,mid,rr

这道题中每个岛的重要度都不同

所以把所有岛构成的权值线段树一个个合并的复杂度总计也只有O(nlogn)O(nlogn)

而两颗完全一样的线段树合并复杂度单次就有O(nlogn)O(nlogn)

这道题合并的步骤很简单

因为只需要查值

一直合并sizsiz直到递归到在某一棵树的叶子节点returnreturn就可以了

试了下启发式合并但效果不大

好吧我个sbsb这种线段树合并和启不启发没有关系的

观察mergemerge可以很好发现这一点

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	char ch=getchar();
	int res=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
	return res*f;
}
const int N=100005;
int n,m,q,a[N],lc[N<<6],rc[N<<6],rt[N<<6],sum[N<<6],fa[N],tot,pos[N];
char c[5];
#define mid ((l+r)>>1)
int find(int x){
	return fa[x]==x?fa[x]:fa[x]=find(fa[x]);
}
void update(int &now,int l,int r,int k){
	if(!now)now=++tot;
	sum[now]++;
	if(l==r)return;
	if(k<=mid)update(lc[now],l,mid,k);
	else update(rc[now],mid+1,r,k);
}
void merge(int &u,int v){
	if(!u||!v){u+=v;return;}
	sum[u]+=sum[v];
	merge(lc[u],lc[v]);
	merge(rc[u],rc[v]);
}
int query(int u,int l,int r,int k){
	if(l==r)return l;
	int del=sum[lc[u]];
	if(k<=del)return query(lc[u],l,mid,k);
	else return query(rc[u],mid+1,r,k-del);
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)a[i]=read(),pos[a[i]]=i,fa[i]=i,update(rt[i],1,n,a[i]);
	for(int i=1;i<=m;i++){
		int u=read(),v=read();
		int f1=find(u),f2=find(v);
		if(f1!=f2)fa[f1]=f2,merge(rt[f2],rt[f1]);//
	}
	int q=read();
	while(q--){
		scanf("%s",c);
		if(c[0]=='B'){
			int u=read(),v=read();
			int f1=find(u),f2=find(v);
			if(f1!=f2)fa[f1]=f2,merge(rt[f2],rt[f1]);
		}
		else{
			int u=read(),k=read();
			u=find(u);
			if(sum[rt[u]]<k)cout<<-1<<'
';
			else cout<<pos[query(rt[u],1,n,k)]<<'
';
		}
	}
}
原文地址:https://www.cnblogs.com/stargazer-cyk/p/10366354.html