bzoj-3123 森林

141 篇文章 0 订阅
88 篇文章 0 订阅
题意:

给出一个n个点m条边的森林,每个点有一个点权,有两种操作;

1.查询两点之间的第K小的点权,保证合法;

2.连边(x,y);

m<n<=80000;

题解:

论正确姿势的重要性;

首先询问和某道COT的题很像,而这道题中多了Link操作;

然而,那道COT的题我是用树链剖分写的。。。

一开始的脑洞是每次将小的暴力重构作为一个轻链连在大的树上,然后每隔一段时间重构一次大树;

听起来十分暴力了。。鬼知道能不能过;

而正解是COT的正解的姿势,将一个询问分成四个,利用主席树的可减性质,找到LCA然后+x+y-LCA(x,y)-FA(LCA(x,y))像这样处理就可以了;

重构是差不多的,也是启发式合并,维护一下倍增LCA;

注意倍增LCA的数组要清。。小心RE,还是说那么写LCA的只有我一个= =?

空间给的足够的大,所以不需要写垃圾回收了,实际上因为每次重构的是整棵子树,回收是可以的但是有些麻烦。。;

总之还是一道挺好的题(笑);


代码:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 81000
#define M 20002000
#define lson l,mid,ls[no]
#define rson mid+1,r,rs[no]
using namespace std;
typedef long long ll;
int next[N<<1],to[N<<1],head[N],ce;
int val[N],dis[N],len;
int deep[N],fa[N][22],root[N];
int size[M],ls[M],rs[M],tot;
char op[10];
namespace Set
{
	int size[N],f[N];
	void init(int n)
	{
		for(int i=1;i<=n;i++)
			size[i]=1,f[i]=i;
	}
	int find(int x)
	{
		return f[x]==x?x:f[x]=find(f[x]);
	}
	void Link(int x,int y)
	{
		x=find(x),y=find(y);
		if(x!=y)
		{
			if(size[x]>size[y])
				swap(x,y);
			size[y]+=size[x];
			f[x]=y;
		}
	}
}
int newnode()
{
	static int ret;
	ret=++tot;
	ls[ret]=rs[ret]=size[ret]=0;
	return ret;
}
void Insert(int l,int r,int &no,int val)
{
	int p=newnode();
	ls[p]=ls[no],rs[p]=rs[no],size[p]=size[no];
	no=p;
	size[no]++;
	if(l==r)	return ;
	int mid=l+r>>1;
	if(val<=mid)	Insert(lson,val);
	else			Insert(rson,val);
}
int query(int l,int r,int no1,int no2,int no3,int no4,int k)
{
	if(l==r)
		return l;
	else
	{
		int mid=l+r>>1;
		if(k<=size[ls[no1]]+size[ls[no2]]-size[ls[no3]]-size[ls[no4]])
			return query(l,mid,ls[no1],ls[no2],ls[no3],ls[no4],k);
		else
			return query(mid+1,r,rs[no1],rs[no2],rs[no3],rs[no4],
				k-size[ls[no1]]-size[ls[no2]]+size[ls[no3]]+size[ls[no4]]);
	}
}
void dfs(int x)
{
	deep[x]=deep[fa[x][0]]+1;
	root[x]=root[fa[x][0]];
	Insert(1,len,root[x],val[x]);
	memset(fa[x]+1,0,sizeof(int)*21);
	for(int i=1;fa[x][i-1];i++)
		fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int i=head[x];i;i=next[i])
	{
		if(to[i]!=fa[x][0])
		{
			fa[to[i]][0]=x;
			dfs(to[i]);
		}
	}
}
int LCA(int x,int y)
{
	if(x==y)	return x;
	if(deep[x]<deep[y])
		swap(x,y);
	int k=20;
	while(k>=0)
	{
		if(deep[fa[x][k]]>=deep[y])
			x=fa[x][k];
		k--;
	}
	if(x==y)	return x;
	k=20;
	while(k>=0)
	{
		if(fa[x][k]!=fa[y][k])
			x=fa[x][k],y=fa[y][k];
		k--;
	}
	return fa[x][0];
}
void add(int x,int y)
{
	to[++ce]=y;
	next[ce]=head[x];
	head[x]=ce;
}
int main()
{
	int n,m,q,i,j,k,x,y,v,fx,fy,last;
	scanf("%*d");
	scanf("%d%d%d",&n,&m,&q);
	Set::init(n);
	for(i=1;i<=n;i++)
		scanf("%d",val+i),dis[i]=val[i];
	sort(dis+1,dis+n+1);
	len=unique(dis+1,dis+n+1)-dis-1;
	for(i=1;i<=n;i++)
		val[i]=lower_bound(dis+1,dis+len+1,val[i])-dis;
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y),add(y,x);
		Set::Link(x,y);
	}
	for(i=1;i<=n;i++)
	{
		if(!deep[i])
			dfs(i);
	}
	for(i=1,last=0;i<=q;i++)
	{
		scanf("%s%d%d",op,&x,&y);
		x^=last,y^=last;
		if(op[0]=='Q')
		{
			scanf("%d",&k);
			k^=last;
			v=LCA(x,y);
			last=dis[query(1,len,root[x],root[y],root[v],root[fa[v][0]],k)];
			printf("%d\n",last);
		}
		else
		{
			fx=Set::find(x),fy=Set::find(y);
			if(Set::size[fx]>Set::size[fy])
				swap(x,y),swap(fx,fy);
			Set::Link(x,y);
			fa[x][0]=y;
			dfs(x);
			add(x,y),add(y,x);
		}
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值