洛谷3302 【SDOI2013】森林(主席树+启发式合并)

版权声明:欢迎转载!请注明出处!qwq https://blog.csdn.net/g21glf/article/details/86632918

传送门

【题目分析】

主席树好题,感谢wcr神犇的讲解。

题意很简单,维护树上路径第k小,将两个块联通,所以自然想到LCT或者主席树。

好像有人用主席树+LCT过了这道题但我不会啊

所以老老实实写主席树吧。

用主席树来维护当前点到所在树的根节点的信息,最后统计的答案就是在x->lca->y的路径上进行统计就行了。

考虑到动态加边这种东东,反正我不知道怎么用树剖做,所以就用倍增了。

明显,主席树没法承受1e9的空间,所以进行离散化。

连边的时候,因为要进行权值线段树的合并,所以来一个启发式合并,将复杂度控制为logn。

然后size小的树的节点的fa要记得赋为size大的树的根。

【代码~】

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;
const int MAXM=2e5+10;

int n,m,q,cnt,last;
int a[MAXN],a1[MAXN],N;
int head[MAXN],fat[MAXN],fa[MAXN][20],siz[MAXN],depth[MAXN],vis[MAXN];
int nxt[MAXM],to[MAXM];
int rt[MAXN],tot;
struct Tree{
	int l,r;
	int siz;
}tr[MAXN*100];

int Read(){
	int i=0,f=1;
	char c;
	for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
	if(c=='-')
	  f=-1,c=getchar();
	for(;c>='0'&&c<='9';c=getchar())
	  i=(i<<3)+(i<<1)+c-'0';
	return i*f;
}

void add(int x,int y){
	nxt[cnt]=head[x];
	head[x]=cnt;
	to[cnt]=y;
	cnt++;
}

int find(int x){
	if(x==fat[x])
	  return x;
	return fat[x]=find(fat[x]);
}

void lisan(){
	sort(a1+1,a1+n+1);
	N=unique(a1+1,a1+n+1)-a1-1;
	for(int i=1;i<=n;++i)
	  a[i]=lower_bound(a1+1,a1+N+1,a[i])-a1;
}

int lca(int x,int y){
	if(depth[x]<depth[y])
	  swap(x,y);
	for(int i=16;i>=0;--i){
		if(depth[fa[x][i]]>=depth[y])
		  x=fa[x][i];
	}
	if(x==y)
	  return x;
	for(int i=16;i>=0;--i){
		if(fa[x][i]!=fa[y][i])
		  x=fa[x][i],y=fa[y][i];
	}
	return fa[x][0];
}

void build(int &root,int l,int r){
	root=++tot;
	if(l==r)
	  return ;
	int mid=l+r>>1;
	build(tr[root].l,l,mid);
	build(tr[root].r,mid+1,r);
}

void insert(int &root,int las,int l,int r,int k){
	root=++tot;
	tr[root]=tr[las];
	tr[root].siz++;
	if(l==r)
	  return ;
	int mid=l+r>>1;
	if(k<=mid)
	  insert(tr[root].l,tr[las].l,l,mid,k);
	else
	  insert(tr[root].r,tr[las].r,mid+1,r,k);
}

int query(int x,int y,int lc,int flc,int l,int r,int k){
	if(l==r)
	  return a1[l];
	int s=tr[tr[x].l].siz+tr[tr[y].l].siz-tr[tr[lc].l].siz-tr[tr[flc].l].siz;
	int mid=l+r>>1;
	if(k<=s)
	  return query(tr[x].l,tr[y].l,tr[lc].l,tr[flc].l,l,mid,k);
	else
	  return query(tr[x].r,tr[y].r,tr[lc].r,tr[flc].r,mid+1,r,k-s);
}

void dfs(int u,int f,int root){
	fa[u][0]=f;
	for(int i=1;i<=16;++i)
	  fa[u][i]=fa[fa[u][i-1]][i-1];
	siz[root]++;
	depth[u]=depth[f]+1;
	fat[u]=root;
	vis[u]=1;
	insert(rt[u],rt[f],1,N,a[u]);
	for(int i=head[u];i!=-1;i=nxt[i]){
		int v=to[i];
		if(v==f)
		  continue;
		dfs(v,u,root);
	}
}

int main(){
	memset(head,-1,sizeof(head));
	int test=Read();
	n=Read(),m=Read(),q=Read();
	for(int i=1;i<=n;++i){
		a[i]=a1[i]=Read();
		fat[i]=i;
	}
	lisan();
	for(int i=1;i<=m;++i){
		int x=Read(),y=Read();
		add(x,y),add(y,x);
	}
	build(rt[0],1,N);
	for(int i=1;i<=n;++i){
		if(!vis[i]){
			dfs(i,0,i);
			fat[i]=i;
		}
	}
	while(q--){
		char cz[3];
		scanf("%s",cz);
		if(cz[0]=='Q'){
			int x=Read()^last,y=Read()^last,k=Read()^last;
			int lc=lca(x,y),flc=fa[lc][0];
			last=query(rt[x],rt[y],rt[lc],rt[flc],1,N,k);
			cout<<last<<'\n';
		}
		else{
			int x=Read()^last,y=Read()^last;
			add(x,y),add(y,x);
			int fx=find(x),fy=find(y);
			if(siz[fx]<siz[fy])
			  swap(x,y),swap(fx,fy);
			dfs(y,x,fx);
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/g21glf/article/details/86632918