洛谷 P3690 Link Cut Tree

题目背景

动态树

题目描述

给定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

说明

数据范围: N,M<=3*10^5.

课上听老师讲的还蛮有道理的,然而调起来简直想打人hhh

不能完全按照splay的套路写。。例如以下的这个错误我调了一个点(...):
我写splay的时候rotate直接判断grandfather(x)如果是0的话就不用连x了,

但是当LCT这么写(我一开始改的是!isroot(fa),但是此时fa的father已经变了,所以gg)会挂。

得改成 if(ch[ffa][0]==fa||ch[ffa][1]==fa) then (balabla)

诸如此类的细节还有很多,,,反正多写写把。。。晚上再搞一道熟练熟练。

/*  若干splay维护树(森林)中的重链 
    
    每颗splay的中序遍历恰好是一条重链
    从上走到下。 
    
    一个节点最多只能有一个偏爱儿子,
    所以在access的时候要先把当前点旋到splay的根,
    把右儿子(也就是在树中的偏爱儿子)断掉,
    换成到access的点路径上的儿子。
*/
#include<bits/stdc++.h>
#define maxn 300005
using namespace std;
int n,m,val[maxn],opt,a,b;
struct LCT{
    int len,ch[maxn][2],f[maxn];
    int tag[maxn],xr[maxn],q[maxn];
    
    inline void init(){
        memset(f,0,sizeof(f));
        memset(ch,0,sizeof(ch));
        memset(tag,0,sizeof(tag));
    }
    
    //更新节点信息 
    inline void update(int x){
        xr[x]=xr[ch[x][0]]^xr[ch[x][1]]^val[x];
    }
    //标记下传 
    inline void pushdown(int x){
        if(tag[x]){
            tag[x]=0,tag[ch[x][0]]^=1,tag[ch[x][1]]^=1;
            swap(ch[x][0],ch[x][1]);
        }
    }
     
    inline int get(int x){
        return ch[f[x]][1]==x;
    }
    //判断一个节点是否为当前splay的根 
    inline bool isroot(int x){
        return (ch[f[x]][0]!=x&&ch[f[x]][1]!=x);
    }
    //splay的旋转操作 
    inline void rotate(int x){
        int fa=f[x],ffa=f[fa],tp=get(x);
        ch[fa][tp]=ch[x][tp^1],f[ch[fa][tp]]=fa;
        ch[x][tp^1]=fa,f[fa]=x;
        f[x]=ffa;
        if(ch[ffa][0]==fa||ch[ffa][1]==fa) ch[ffa][ch[ffa][1]==fa]=x;
        update(fa),update(x);
    }
    //旋到根 
    inline void splay(int x){
        len=0;
        for(int i=x;;i=f[i]){
            q[++len]=i;
            if(isroot(i)) break;
        } 
        for(;len;len--) pushdown(q[len]);
        
        while(!isroot(x)){
            int y=f[x],z=f[y];
            if(!isroot(y)) rotate((get(y)==get(x)?y:x));
            rotate(x);
        }
        
    }
    //将一个点到它所在的树的根的路径上的边都变成重边 
    inline void access(int x){
        for(int son=0;x;son=x,x=f[x]) splay(x),ch[x][1]=son,update(x);
    }
    //把一个节点搞成它所在树的根 
    inline void makeroot(int x){
        access(x);
        splay(x),tag[x]^=1;
    }
    //找所在树的根
    inline int find_root(int x){
        access(x),splay(x);
        while(ch[x][0]) x=ch[x][0];
        return x;
    }
    //把y所在的树从y点接到x点上 
    inline void add(int x,int y){
        makeroot(x),f[x]=y;
        //不能更新,因为y不是x的重儿子 
    }
    //把x和y点断开
    inline void cut(int x,int y){
        makeroot(x),access(y);
        splay(y);
        if(ch[y][0]==x) ch[y][0]=0,f[x]=0;
    } 
}M;

int main(){
    M.init(); 
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",val+i),M.xr[i]=val[i];
    while(m--){
        scanf("%d%d%d",&opt,&a,&b);
        if(!opt){
            M.makeroot(a),M.access(b),M.splay(b);
            printf("%d
",M.xr[b]);
        }
        else if(opt==1){
            if(M.find_root(a)==M.find_root(b)) continue;
            M.add(a,b);
        }
        else if(opt==2){
            if(M.find_root(a)!=M.find_root(b)) continue;
            M.cut(a,b);
        }
        else{
            M.access(a),M.splay(a);
            val[a]=b,M.update(a);
        }
    }
    
    return 0;
}
原文地址:https://www.cnblogs.com/JYYHH/p/8317849.html