洛谷3302 BZOJ3123 SDOI2013 森林 主席树启发式合并

版权声明:本文为博主原创文章,可以转载但是必须声明版权。 https://blog.csdn.net/forever_shi/article/details/84573335

题目链接
题意:
给你一个森林,点有点权,有两种操作,一种是把x与y之间连一条边,保证连边之后仍然是树形结构;一种是询问x到y的路径上第k大的点权是多少,保证x到y连通并且路径上有第k大的点。n<=80000

题解;
如果没有连边,那么就是count on tree那道题,可以在这里

那么我们就考虑连边怎么处理。常见的支持连边的东西有并查集、LCT和启发式合并以及暴力合并。如果暴力合并肯定复杂度不对,这题据说是可以LCT的,但是写起来感觉会比较麻烦,我用并查集和启发式合并来维护连边操作。

具体地我们对于每次连边,我们可以用并查集维护每个点的连通情况,并且顺便记录一下每个连通块的大小。我们根据连通块的大小,进行启发式合并,我们比较暴力地重新对比较小的那棵主席树进出重构,重构的过程还有重新求一遍倍增数组和每个点在树中的深度。具体看看代码实现吧,没有其他的思维难度了。

代码:

#include <bits/stdc++.h>
using namespace std;

int n,m,T,hed[500010],cnt,fa[500010],f[200010][21],b[200010],c[200010],ji,sz[200010];
int s[20000100],root[2000010],ans,vis[2000010],dep[2000010],ls[20000100],rs[20000100],cnt1;
struct node
{
	int to,next;
}a[800010];
char opt;
inline void add(int from,int to)
{
	a[++cnt].to=to;
	a[cnt].next=hed[from];
	hed[from]=cnt;
}
inline int getr(int x)
{
	if(x==fa[x])
	return x;
	fa[x]=getr(fa[x]);
	return fa[x];
} 
inline void update(int &rt,int l,int r,int x)
{
	int mid=(l+r)>>1;
	s[++cnt1]=s[rt]+1;
	ls[cnt1]=ls[rt];
	rs[cnt1]=rs[rt];
	rt=cnt1;
	if(l==r)
	return;
	if(x<=mid)
	update(ls[rt],l,mid,x);
	else
	update(rs[rt],mid+1,r,x);
}
inline void dfs(int x)
{
	vis[x]=1;
	dep[x]=dep[f[x][0]]+1;
	root[x]=root[f[x][0]];
	update(root[x],1,ji,b[x]);
	for(int i=1;i<=20;++i)
	f[x][i]=f[f[x][i-1]][i-1];
	for(int i=hed[x];i;i=a[i].next)
	{
		int y=a[i].to;
		if(y==f[x][0])
		continue;
		int fx=getr(x),fy=getr(y);
		fa[fy]=fx;
		sz[fx]+=sz[fy];
		f[y][0]=x;
		dfs(y);		
	}
}
inline int lca(int x,int y)
{
	if(dep[x]<dep[y])
	swap(x,y);
	for(int i=20;i>=0;--i)
	{
		if(dep[x]-dep[y]>=(1<<i))
		x=f[x][i];
	}
	if(x==y)
	return x;
	for(int i=20;i>=0;--i)
	{
		if(f[x][i]!=f[y][i]&&dep[f[x][i]])
		{
			x=f[x][i];
			y=f[y][i];
		}
	}
	return f[x][0]; 
}
inline int query(int x,int y,int xx,int yy,int l,int r,int k)
{
	if(l==r)
	return l;
	int mid=(l+r)>>1;
	int gg=s[ls[x]]+s[ls[y]]-s[ls[xx]]-s[ls[yy]];
	if(gg>=k)
	return query(ls[x],ls[y],ls[xx],ls[yy],l,mid,k);
	else
	return query(rs[x],rs[y],rs[xx],rs[yy],mid+1,r,k-gg);
}
inline void insert(int x,int y)
{
	dep[x]=dep[f[x][0]]+1;
	root[x]=root[f[x][0]];
	update(root[x],1,ji,b[x]);
	for(int i=1;i<=20;++i)
	f[x][i]=f[f[x][i-1]][i-1];
	for(int i=hed[x];i;i=a[i].next)
	{
		int y=a[i].to;
		if(y==f[x][0])
		continue;
		f[y][0]=x;
		insert(y,x);
	}
}
int main()
{
	scanf("%d",&n);
	scanf("%d%d%d",&n,&m,&T);
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&b[i]);
		c[i]=b[i];
	}
	for(int i=1;i<=m;++i)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	sort(c+1,c+n+1);
	ji=unique(c+1,c+n+1)-c-1;
	for(int i=1;i<=n;++i)
	b[i]=lower_bound(c+1,c+ji+1,b[i])-c;
	for(int i=1;i<=n;++i)
	{
		fa[i]=i;
		sz[i]=1;
	}	
	for(int i=1;i<=n;++i)
	{
		if(!vis[i])
		dfs(i);
	}
	for(int i=1;i<=T;++i)
	{
		opt=getchar();
		while(opt!='Q'&&opt!='L')
		opt=getchar();
		int x,y,z;
		if(opt=='Q')
		{
			scanf("%d%d%d",&x,&y,&z);
			x^=ans;
			y^=ans;
			z^=ans;
			int xx=lca(x,y),yy=f[xx][0]; 
			ans=query(root[x],root[y],root[xx],root[yy],1,ji,z);
			ans=c[ans];
			printf("%d\n",ans);
		}
		else
		{
			scanf("%d%d",&x,&y);
			x^=ans;
			y^=ans;
			int fx=getr(x),fy=getr(y);
			if(sz[fx]>sz[fy])
			{
				swap(x,y);
				swap(fx,fy);
			}
			f[x][0]=y;
			insert(x,y);
			add(x,y);
			add(y,x);
			fa[fx]=fy;
			sz[fy]+=sz[fx];
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/84573335
今日推荐