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,109
1
,
10
9
]内,代表每个点的权值。
第N+2行到第N+M+1行,每行三个整数,分别代表操作类型和操作所需的量。
1≤N,M≤300000
1
≤
N
,
M
≤
300000
Output
对于每一个0号操作,你须输出X到Y的路径上点权的Xor和。
Sample Input
3 3
1
2
3
1 1 2
0 1 2
0 1 1
Sample Output
3
LCT的模板题,学splay和LCT合起来花了两三天
LCT的核心就是用splay维护preferred path,preferred path定义为上次access操作所经过的那些边,access操作的本质是打通当前点到树的根
这题要求删边,加边和维护一条链上的xor和
对于加边(u,v):makeroot(u),fa[u]=v即可,注意虽然这时fa[u]=v,但是u并不是v的儿子,u和v本质上属于两棵splay
对于删边(u,v):makeroot(u),access(v),splay(v),此时u一定是v的左儿子,断掉就行
对于查询(u,v):makeroot(u),access(v),splay(v),这时u是原树的根,v是splay的根,且因为我刚刚做过access操作,所以v所在的splay一定是u到v的路径上的点组成的splay,直接输出sum(v)即可
几个代码易错点:
1. rotate操作和纯splay的rotate操作有一点小小的不一样,LCT的splay中如果y是根,那么不能把z的儿子置为x,因为此时z和x不在一棵splay中,在纯splay中如果y是根,z必然是0,没有这个锅
2. splay操作和纯splay的splay操作有一点小小的不一样
LCT的splay操作判断根的方式是看父亲为0或父亲的孩子不是自己,纯splay只要看父亲是不是0就行
LCT的splay操作中如果开始旋转的点就是当前splay的根,要记得先pushdown一下,因为后面会有其他的splay接到这个点下面,不提前下传标记就混乱了,纯splay中没有这种操作
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define LB long double
#define ull unsigned long long
#define x first
#define y second
#define pb push_back
#define pf push_front
#define mp make_pair
#define Pair pair<int,int>
#define pLL pair<LL,LL>
#define pii pair<double,double>
const int INF=2e9;
const LL LINF=2e16;
const int magic=348;
const int MOD=998244353;
const double eps=1e-10;
const double pi=acos(-1);
inline int getint()
{
bool f;char ch;int res;
while (!isdigit(ch=getchar()) && ch!='-') {}
if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
while (isdigit(ch=getchar())) res=res*10+ch-'0';
return f?res:-res;
}
const int MAXN=3e5;
namespace LCT
{
struct node
{
int ch[2],val,father;
int flip,xsum;
}tree[MAXN*2];int tot;
inline void Create(int lson,int rson,int val,int father)
{
++tot;
tree[tot].ch[0]=lson;tree[tot].ch[1]=rson;
tree[tot].val=val;tree[tot].father=father;
tree[tot].flip=0;tree[tot].xsum=val;
}
inline void pushup(int cur)
{
tree[cur].xsum=(tree[cur].val^tree[tree[cur].ch[0]].xsum^tree[tree[cur].ch[1]].xsum);
}
inline void pushdown(int cur)
{
if (tree[cur].flip)
{
tree[tree[cur].ch[0]].flip^=1;
tree[tree[cur].ch[1]].flip^=1;
swap(tree[cur].ch[0],tree[cur].ch[1]);
tree[cur].flip=0;
}
}
inline bool isroot(int cur) {return !tree[cur].father || (tree[tree[cur].father].ch[0]!=cur && tree[tree[cur].father].ch[1]!=cur);}
inline void rotate(int x)
{
int y=tree[x].father,z=tree[y].father;
pushdown(y);pushdown(x);
int k=(tree[y].ch[1]==x);
if (!isroot(y)) tree[z].ch[tree[z].ch[1]==y]=x;
tree[y].ch[k]=tree[x].ch[k^1];
tree[x].ch[k^1]=y;
tree[tree[y].ch[k]].father=y;tree[y].father=x;tree[x].father=z;
pushup(y);pushup(x);
}
inline void splay(int x)
{
pushdown(x);
while (!isroot(x))
{
int y=tree[x].father,z=tree[y].father;
if (!isroot(y))
((tree[y].ch[1]==x)^(tree[z].ch[1]==y))?rotate(x):rotate(y);
rotate(x);
}
}
inline void access(int cur)
{
for (int pre=0;cur;pre=cur,cur=tree[cur].father)
{
splay(cur);
tree[cur].ch[1]=pre;
pushup(cur);
}
}
inline void makeroot(int x)
{
access(x);splay(x);
tree[x].flip^=1;
}
inline int find(int x)
{
access(x);splay(x);
while (tree[x].ch[0]) x=tree[x].ch[0];
return x;
}
inline void link(int u,int v) // u is v's son
{
makeroot(u);
tree[u].father=v;
}
inline void cut(int u,int v)
{
makeroot(u);
access(v);splay(v);
if (tree[v].ch[0]==u)
tree[v].ch[0]=0,tree[u].father=0;
}
inline void update(int x,int val)
{
makeroot(x);
tree[x].val=val;
pushup(x);
}
inline int query(int u,int v)
{
makeroot(u);
access(v);splay(v);
return tree[v].xsum;
}
}
int n,q;
int a[MAXN+48];
int main ()
{
int i,op,x,y;n=getint();q=getint();
for (i=1;i<=n;i++) a[i]=getint(),LCT::Create(0,0,a[i],0);
while (q--)
{
op=getint();x=getint();y=getint();
if (op==0) printf("%d\n",LCT::query(x,y));
if (op==1) LCT::link(x,y);
if (op==2) LCT::cut(x,y);
if (op==3) LCT::update(x,y);
}
return 0;
}