题目背景
动态树
题目描述
给定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。
输入输出格式
输入格式:
第1行两个整数,分别为n和m,代表点数和操作数。
第2行到第n+1行,每行一个整数,整数在[1,10^9]内,代表每个点的权值。
第n+2行到第n+m+1行,每行三个整数,分别代表操作类型和操作所需的量。
输出格式:
对于每一个0号操作,你须输出x到y的路径上点权的xor和。
输入输出样例
输入样例#1: 复制
3 3
1
2
3
1 1 2
0 1 2
0 1 1
输出样例#1: 复制
3
1
#include<bits/stdc++.h>
using namespace std;
int read(){
char ch=getchar();
int h=0;
while(ch>'9'||ch<'0')ch=getchar();
while(ch>='0'&&ch<='9'){h=h*10+ch-'0';ch=getchar();}
return h;
}
const int MAXN=300001;
int N,M,zhan[MAXN],top=0;
struct Splay{int val,sum,rev,ch[2],fa;}t[MAXN];
void pushup(int x){ //向上维护异或和
t[x].sum=t[t[x].ch[0]].sum^t[t[x].ch[1]].sum^t[x].val;//异或和
}
void reverse(int x){
swap(t[x].ch[0],t[x].ch[1]);
t[x].rev^=1;//标记表示已经翻转了该点的左右儿子
}
void pushdown(int x){//向下传递翻转标记
if(!t[x].rev)return;
if(t[x].ch[0])reverse(t[x].ch[0]);
if(t[x].ch[1])reverse(t[x].ch[1]);
t[x].rev=0;
}
bool isroot(int x){
return t[t[x].fa].ch[0]!=x&&t[t[x].fa].ch[1]!=x;
}
void rotate(int x){ //Splay向上操作
int y=t[x].fa,z=t[y].fa;
int k=t[y].ch[1]==x;
if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;//Attention if()
t[x].fa=z;//注意了
t[y].ch[k]=t[x].ch[k^1];t[t[x].ch[k^1]].fa=y;
t[x].ch[k^1]=y;t[y].fa=x;
pushup(y);
}
void splay(int x){ //把x弄到根
zhan[++top]=x;
for(int pos=x;!isroot(pos);pos=t[pos].fa)zhan[++top]=t[pos].fa;
while(top)pushdown(zhan[top--]);
while(!isroot(x))
{
int y=t[x].fa,z=t[y].fa;
if(!isroot(y)) (t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
rotate(x);
}
pushup(x);
}
void Access(int x){
for(int y=0;x;y=x,x=t[x].fa){splay(x);t[x].ch[1]=y;pushup(x);}
}
void makeroot(int x){//函数功能:把x拎成原图的根
Access(x);splay(x);//把x和根先弄到一起
reverse(x);//然后打区间翻转标记,应该在根的地方打但是找不到根所以要splay(x)
}
int findroot(int x){//函数功能:找到x所在联通块的splay的根
Access(x);splay(x);
while(t[x].ch[0])x=t[x].ch[0];
return x;
}
void split(int x,int y){//函数功能:把x到y的路径抠出来
makeroot(x);//先把x弄成原图的根
Access(y);//再把y和根的路径弄成重链
splay(y);//那么就是y及其左子树存储的信息了
}
void link(int x,int y){//函数功能:连接x,y所在的两个联通块
makeroot(x);//把x弄成其联通块的根
t[x].fa=y;//连到y上(虚边)
}
void cut(int x,int y){//函数功能:割断x,y所在的两个联通块
split(x,y);
if (t[x].fa == y && !t[x].ch[1]){
t[y].ch[0]=t[x].fa=0;
pushup(y);
}
}
int main()
{
N=read();M=read();
for(int i=1;i<=N;i++)
t[i].val=read();
for(int i=1;i<=M;i++)
{
int op=read(),x=read(),y=read();
if(op==0){
split(x,y);//抠出路径
printf("%d\n",t[y].sum);
}
if(op==1){
if(findroot(x)^findroot(y))
link(x,y);//x,y不在同一联通块里
}
if(op==2) {
if (!findroot(x)^findroot(y))
cut(x,y);//x,y在同一联通块
}
if(op==3) {//把x点的权值改成y
Access(x);//把x到根的路径设置为重链
splay(x);//把x弄到该链的根结点
t[x].val=y;
pushup(x);//直接改x的val并更新
}
}
return 0;
}