uoj#207. 共价大爷游长沙

3 篇文章 0 订阅
3 篇文章 0 订阅

miaom又来做LCT了!//lych:无敌

由于不是很懂LCT子树信息维护的那套理论,想了好久(搞得树剖的子树维护就会了一样!)。

首先是一个很巧妙的转化——将边在链上转化为这条边能将链的顶点分开。给每组顶赋一个随机权,每次可以把边断掉,查询每个联通块内权值异或和是否等于所有顶点权值异或和,就是子树异或和。

然后发现动态树上子树不是dfs序连续那么简单,他应该是(当前点及沿偏爱边往下走得到的点)以及他们的虚边的子树。然后维护一些值(splay 区间虚儿子&自己和
      单点&虚儿子和)。

LCT的代码来自@lbn187板子的默写,如有错误不负责。。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#define N 300005
using namespace std;
int id,n,m,x,y,p,qx[N],qy[N],now,val[N],ly;
int c[N][2],fa[N],v[N],s[N],rev[N];
//子树权值异或和
//splay 区间虚儿子&自己和 s
//单点&虚儿子和 v
//单点修改
bool RT(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
void rever(int x){rev[x]^=1;swap(c[x][0],c[x][1]);}
void up(int x)
{
	s[x]=s[c[x][0]]^s[c[x][1]]^v[x];
}
void dn(int x)
{
	if (rev[x])
		rev[x]=0,rever(c[x][0]),rever(c[x][1]);
}
void pd(int x){if (!RT(x)) pd(fa[x]);dn(x);}
void rot(int x)
{
	int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
	if (!RT(y)) c[z][c[z][1]==y]=x;
	fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
	c[y][l]=c[x][r];c[x][r]=y;up(y);up(x);
}
void splay(int x,int y=0)
{
	for (pd(x);!RT(x);rot(x))
		if (!RT(y=fa[x]))
			rot(c[y][0]==x^c[fa[y]][0]==y?x:y);
}
void acc(int x)
{
	for (int y=0;x;y=x,x=fa[x])
		splay(x),v[x]^=s[c[x][1]]^s[y],c[x][1]=y,up(x);
}
void MRt(int x){acc(x);splay(x);rever(x);}
void link(int x,int y){MRt(x);MRt(y);v[y]^=s[x];s[y]^=s[x];fa[x]=y;}
void cut(int x,int y){MRt(x);acc(y);splay(y);c[y][0]=fa[x]=0;up(y);}
void mdy(int x,int y){MRt(x);v[x]^=y;s[x]^=y;}
int qry(int x,int y){MRt(x);acc(y);return v[y]+s[c[y][1]];}
int main()
{
	int id;
	scanf("%d",&id);
	scanf("%d%d",&n,&m);
	for (int i=2;i<=n;i++)
		scanf("%d%d",&x,&y),link(x,y);
	for (int i=1;i<=m;i++)
	{
		scanf("%d",&p);
		if (p==1)
		{
			scanf("%d%d",&x,&y);
			cut(x,y);
			scanf("%d%d",&x,&y);
			link(x,y);
		}
		else if (p==2)
		{
			scanf("%d%d",&x,&y);
			val[++ly]=(rand()<<16)^rand();
			qx[ly]=x;qy[ly]=y;
			now^=val[ly];
			mdy(x,val[ly]);
			mdy(y,val[ly]);
		}
		else if (p==3)
		{
			scanf("%d",&x);
			now^=val[x];
			mdy(qx[x],val[x]);
			mdy(qy[x],val[x]);
		}
		else if (p==4)
		{
			scanf("%d%d",&x,&y);
			puts(qry(x,y)==now?"YES":"NO");
		}
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值