【UOJ207】共价大爷游长沙【LCT】【异或】【随机化】

本文介绍了一种使用LCT树和随机化算法解决无权树上路径操作问题的方法,包括断边连边、路径集合操作及路径询问。通过为路径赋予随机权值并利用异或运算,算法能在O(nlogn)的时间复杂度下实现高精度的解答。

传送门

题意:维护一棵无权树和一个路径集合 S S S,支持以下操作:

  1. 断边连边
  2. S S S加入中加入一条路径
  3. 删除 S S S中的一条路径
  4. 询问是否 S S S中的所有路径都经过了边 ( x , y ) (x,y) (x,y)

n ≤ 1 0 5 , q ≤ 3 × 1 0 5 n\leq10^5,q\leq3\times10^5 n105,q3×105

给每条加入的路径随机赋一个权值并把端点异或上这个权值

这样如果所有路径都跨越了 u u u的子树, u u u子树的异或和就等于所有路径的异或和

用LCT维护

出错的可能性是有若干条子树内或外的路径异或起来等于 0 0 0,这个概率与路径条数无关,为 1 V \frac{1}{V} V1( V V V为随机的值域)

当在int范围内随机时, 3 × 1 0 5 3\times 10^5 3×105次询问都不出错的概率约为 99.986 % 99.986\% 99.986%,可以通过

复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

注意link的时候要split(即把 y y y变成辅助树上的真根),因为虚子树会影响 y y y祖先的信息

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cstdlib>
#define MAXN 100005
#define MAXM 300005
using namespace std;
inline int read()
{
	int ans=0;
	char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline int Rand(){return (rand()<<15)^rand();}
int ch[MAXN][2],fa[MAXN],rv[MAXN],val[MAXN],s[MAXN],si[MAXN];
inline void update(int x){s[x]=val[x]^s[ch[x][0]]^s[ch[x][1]]^si[x];}
inline void pushr(int x){swap(ch[x][0],ch[x][1]);rv[x]^=1;}
inline void pushdown(int x)
{
	if (rv[x])
	{
		if (ch[x][0]) pushr(ch[x][0]);
		if (ch[x][1]) pushr(ch[x][1]);
		rv[x]=0;	
	} 
}
inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline int get(int x){return ch[fa[x]][1]==x;}
inline void rotate(int x)
{
	int y=fa[x],z=fa[y];
	int l=get(x),r=l^1;
	int w=ch[x][r];
	if (!isroot(y)) ch[z][get(y)]=x;
	ch[x][r]=y,ch[y][l]=w;
	if (w) fa[w]=y;
	fa[y]=x,fa[x]=z;
	update(y),update(x);
}
int q[MAXN],tp;
inline void splay(int x)
{
	q[tp=1]=x;
	for (int i=x;i;i=fa[i]) q[++tp]=fa[i];
	for (int i=tp;i>=1;i--) pushdown(q[i]);
	while (!isroot(x))
	{
		int y=fa[x];
		if (!isroot(y))
		{
			if (get(x)==get(y)) rotate(y);
			else rotate(x);
		}
		rotate(x);
	}
}
inline void access(int x)
{
	for (int y=0;x;y=x,x=fa[x])
	{
		splay(x);
		si[x]^=s[ch[x][1]];
		si[x]^=s[ch[x][1]=y];
		update(x);
	}
}
inline void evert(int x){access(x),splay(x),pushr(x);}
inline void split(int x,int y){evert(x),access(y),splay(y);}
inline void link(int x,int y){split(x,y),fa[x]=y,si[y]^=s[x],update(y);}
inline void cut(int x,int y){split(x,y);ch[y][0]=fa[x]=0;update(y);}
inline int query(int x,int y){split(x,y);return si[y]^val[y];}
inline void modify(int x,int v){access(x),splay(x),val[x]^=v,update(x);}
int x[MAXM],y[MAXM],v[MAXM],cur,cnt;
int main()
{
	read();
	int n,m;
	n=read(),m=read();
	for (int i=1;i<n;i++)
	{
		int u,v;
		u=read(),v=read();
		link(u,v);
	}
	while (m--)
	{
		int type=read();
		if (type==1)
		{
			int x,y,u,v;
			x=read(),y=read(),u=read(),v=read();
			cut(x,y),link(u,v);
		}
		if (type==2)
		{
			++cnt;
			x[cnt]=read(),y[cnt]=read(),v[cnt]=Rand();
			modify(x[cnt],v[cnt]),modify(y[cnt],v[cnt]);
			cur^=v[cnt];
		}
		if (type==3)
		{
			int k=read();
			modify(x[k],v[k]),modify(y[k],v[k]);
			cur^=v[k];
		}
		if (type==4)
		{
			int x,y;
			x=read(),y=read();
			puts(query(x,y)==cur? "YES":"NO");
		}
	}
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值