题目给出N个点和m个查询,每次查询对n个点添加或者删除边,但是要保证在联通的情况下,因此无论怎么操作,他都保证是一棵树。
LCT 处理一棵树上的边的添加和删除。
LCT把一棵树分成很多条链,每一条链使用splay来维护,splay之间相关连接。 splay使用这条链上的节点的深度来维护。
每当需要查询x到y的路径时,通过access操作,把x和y这条路径组成一棵新的splay,其他点则变成自己独立的splay。这个splay就可以做树dp来统计路径上的信息。
Access(x)
将x变成所在的splay的root,然后挂在他父节点上(父节点是所在splay的根)。
由于splay维护的是树的深度,每次splay(x)都会把比当前节点深度大的节点断开,连接上另外一棵树,行程新的链。最后得出一条从根节点到x的splay。
void access(int x){
int y = 0;
while(x){
splay(x); son[x][1] = y; y = x;
pushup(x);
x = f[x];
}
}
MakeRoot(x)
使x变成整个树的根。
先Access(x)使得根和x在同一条链上,然后splay(x)把x转到根。由于Access(x)是形成一条根到x的链,x为最大深度,因此需要把splay的每一个节点的左右儿子互换,翻转整个树。互换儿子只是打标记,儿子操作在pushdown的时候带下去。
void make_root(int x){
access(x);
splay(x);
switch_son(x);
}
Splay(x)
Splay 之前需要把从根到当前节点的信息都pushdown下来。
然后就是不断的转了。转的时候要父和父父节点,如果父节点不是根,则转一次,然后再转x。
void splay(int x){
//push down first
int y = x;
stack<int> st;
st.push(y);
while(!isroot(y)) st.push(f[y]),y=f[y];
while(!st.empty()) {
pushdown(st.top()),st.pop();
}
while(!isroot(x)){
int y = f[x];
int z = f[y];
if(!isroot(y)){
rotate(y);
}
rotate(x);
}
pushup(x);
}
Link(x,y)
连接x和y。 先把x变成root(每次操作都要先变成root),然后判断一下y的根是不是x。如果已经是了那么已经连通,不是就直接把父亲指向他。
void link(int x, int y){
make_root(x);
if(findroot(y)==x)return;
f[x] = y;
}
Cut(x,y)
把两个点之间的边断开。首先把x变成root,然后access(y)把x和y放到同一棵splay上。由于access的时候x不是根了,所以再做一次splay。(x不是根了,y的父节点可能就不是x了吗,所以要先把x换成根)
然后判断y的父节点是不是x,y的左还是还有没其他节点(有的话说明x和y之间有其他节点,深度在x和y之间)
void cut(int x, int y){
make_root(x);
access(y);
// if(findroot(y)!=x) return;
splay(x);
if(f[y] != x || son[y][0]) return; //very tricky
f[y] = 0;
son[x][1] =0;
pushup(x);
}
查询(x,y)之间的异或和
把x变成root,然后access(y)放到一条链上,splay(y)做一次从y到x的值的更新。
注意这个splay只包含x到y路径上的点,因此最后根的值就是整个树的值。
int query(int x, int y){
make_root(x);
access(y);
splay(y);
return sum[y];
}
修改(x)的值为y
把x转到根,因为不能修改中间。如果查询的时候splay没有动过,则根节点的值不会被更新。
void modify(int x, int v){
splay(x);
a[x] = v;
pushup(x);
}
完整代码
int son[N][2], f[N];
int sum[N],r[N];
int a[N];
int n,m;
void pushup(int x){
sum[x] = sum[son[x][0]] ^ sum[son[x][1]] ^ a[x];
}
void switch_son(int x){
int t = son[x][0]; son[x][0] = son[x][1];son[x][1] = t;
r[x]^=1;
}
void pushdown(int x){
if(r[x]){
switch_son(son[x][0]);
switch_son(son[x][1]);
r[x] = 0;
}
}
bool isroot(int x){
return !(son[f[x]][0]==x || son[f[x]][1]==x);
}
void rotate(int x){
int y = f[x], z = f[y];
if(!isroot(y)){
son[z][son[z][1]==y] = x;
}
int p = son[y][1]==x;
int t = son[x][!p];
son[x][!p] = y;
son[y][p] = t;
f[x] = z; f[y] = x; if(t)f[t] = y;
pushup(y);
}
void splay(int x){
//push down first
int y = x;
stack<int> st;
st.push(y);
while(!isroot(y)) st.push(f[y]),y=f[y];
while(!st.empty()) {
pushdown(st.top()),st.pop();
}
while(!isroot(x)){
int y = f[x];
int z = f[y];
if(!isroot(y)){
rotate(y);
}
rotate(x);
}
pushup(x);
}
// a chain from root to node x.
void access(int x){
int y = 0;
while(x){
splay(x); son[x][1] = y; y = x;
pushup(x);
x = f[x];
}
}
int findroot(int x){
int xx =x;
access(x);
splay(x);
while(son[x][0]) {
pushdown(x);
x = son[x][0];
}
return x;
}
void make_root(int x){
access(x);
splay(x);
switch_son(x);
}
void link(int x, int y){
make_root(x);
if(findroot(y)==x)return;
f[x] = y;
}
void cut(int x, int y){
make_root(x);
access(y);
// if(findroot(y)!=x) return;
splay(x);
if(f[y] != x || son[y][0]) return; //very tricky
f[y] = 0;
son[x][1] =0;
pushup(x);
}
int query(int x, int y){
make_root(x);
access(y);
splay(y);
return sum[y];
}
void modify(int x, int v){
splay(x);
a[x] = v;
pushup(x);
}
int main(){
sf("%d%d",&n,&m);
fr(i,1,n+1)sf("%d",&a[i]);
fr(i,0,m){
int op,x,y;
sf("%d%d%d",&op,&x,&y);
if(op==0){
pf("%d\n",query(x,y));
}
else if(op==1){
link(x,y);
}
else if(op==2){
cut(x,y);
}
else {
modify(x,y);
}
}
}