BZOJ3282 Tree

Description

  给定N个点以及每个点的权值,要你处理接下来的M个操作。操作有4种。操作从0到3编号。点从1到N编号。
  0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和。保证x到y是连通的。
  1:后接两个整数(x,y),代表连接x到y,若x到y已经连通则无需连接。
  2:后接两个整数(x,y),代表删除边(x,y),不保证边(x,y)存在。
  3:后接两个整数(x,y),代表将点x上的权值变成y。

Input

第1行两个整数,分别为N和M,代表点数和操作数。
第2行到第N+1行,每行一个整数,整数在[1,10^9]内,代表每个点的权值。
第N+2行到第N+M+1行,每行三个整数,分别代表操作类型和操作所需的量。

Output

对于每一个0号操作,你须输出X到Y的路径上点权的Xor和。

Sample Input

3 3

1

2

3

1 1 2

0 1 2

0 1 1

Sample Output

3

1

Hint

1<=N,M<=300000

简单的维护点权信息,裸题。(LCT取链操作比较重要)

#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,r) for(register int i=(L);i<=(r);++i)
const int N = 3e5+10;
int n,m;
struct Splay{
	bool rev[N];
	int p[N],ch[N][2];
	int vl[N],sum[N];
	#define Ls(v) ch[v][0]
	#define rs(v) ch[v][1]
	inline bool is_root(int x){
		return (Ls(p[x])^x)&&(rs(p[x])^x);
	}
	inline void maintain(int x){
		sum[x]=sum[Ls(x)]^vl[x]^sum[rs(x)];
	}
	inline void pushdown(int x){
		if(rev[x]){
			rev[Ls(x)]^=1,rev[rs(x)]^=1;
			swap(Ls(x),rs(x));
			rev[x]=0;
		}
	}
	inline void rot(int x){
		int f=p[x],gf=p[f],type=rs(f)==x,son=ch[x][!type];
		if(!is_root(f))ch[gf][rs(gf)==f]=x;p[x]=gf;
		ch[p[son]=f][type]=son,maintain(f);
		ch[p[f]=x][!type]=f,maintain(x);
	}
	int top,stk[N];
	inline void splay(int x){
		stk[++top]=x;
		if(!is_root(x))for(int i=x;!is_root(i);i=p[i])stk[++top]=p[i];
		while(top)pushdown(stk[top--]);
		while(!is_root(x)){
			if(is_root(p[x]))return rot(x),void();
			if((rs(p[p[x]])==p[x])==(rs(p[x])==x))rot(p[x]);
			rot(x);
		}
	}
	inline int findrt(int x){
		while(Ls(x))x=Ls(x);
		return x;
	}
};
struct LCT{
	Splay sp;
	inline void access(int x){
		for(int lastx=0;x;lastx=x,x=sp.p[x])
			sp.splay(x),sp.rs(x)=lastx,sp.maintain(x);
	}
	inline void make_root(int x){
		access(x),sp.splay(x),sp.rev[x]^=1;
	}
	inline void link(int x,int y){
		make_root(x),sp.p[x]=y;
	}
	inline void cut(int x,int y){
		make_root(x),access(y),sp.splay(y);
		sp.p[x]=sp.Ls(y)=0;
	}
	inline void split(int x,int y){
		make_root(x),access(y),sp.splay(y);
	}
	inline bool connect(int x,int y){
		int rtx,rty;
		access(x),sp.splay(x),rtx=sp.findrt(x);
		access(y),sp.splay(y),rty=sp.findrt(y);
		return rtx==rty;
	}
}lct;
inline void init(){
	scanf("%d%d",&n,&m);
	Inc(i,1,n)scanf("%d",&lct.sp.vl[i]);
}
inline void solv(){
	while(m--){
		int op,x,y;scanf("%d%d%d",&op,&x,&y);
		switch(op){
			case 0:lct.split(x,y);cout<<lct.sp.sum[y]<<"\n";break;
			case 1:if(!lct.connect(x,y))lct.link(x,y);break;
			case 2:if(lct.connect(x,y))lct.cut(x,y);break;
			case 3:lct.sp.vl[x]=y,lct.sp.splay(x),lct.sp.maintain(x);break;
		}
	}
}
int main(){
	init();
	solv();
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值