【洛谷P3224】【HNOI2012】—永无乡(线段树合并)
线段树合并板子题
线段树合并的复杂度要根据两颗树的相似度而定的
又因为一颗线段树的值域确定了,其形状也就确定了(只要你是好好写的,)
这道题中每个岛的重要度都不同
所以把所有岛构成的权值线段树一个个合并的复杂度总计也只有
而两颗完全一样的线段树合并复杂度单次就有
这道题合并的步骤很简单
因为只需要查值
一直合并直到递归到在某一棵树的叶子节点就可以了
试了下启发式合并但效果不大
好吧我个这种线段树合并和启不启发没有关系的
观察可以很好发现这一点
#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<<'\n';
else cout<<pos[query(rt[u],1,n,k)]<<'\n';
}
}
}