洛谷 P3690 【【模板】Link Cut Tree (动态树)】

阅读前请确保你会\(\text{splay}\)
\(\text{LinkCutTree}\)模板,简称\(\text{lct}\)
首先我们把一棵树里所有节点都全部划分在一些\(\text{splay}\)里,每一个\(\text{splay}\)维护的东西都是一条在原树上从上到下按深度递增的一条链。
然后我们定义每条边有两种类型:实或者虚。
其中实边代表的是在某个\(\text{splay}\)内部的边,而虚边则是某一个\(\text{splay}\)所指向另一个节点所对应的边。
然后这就是所谓的“实链剖分”了。而我们大家所熟知的树剖则是“重链剖分”。
然后我们再来看一看各种操作。
\(\text{access}\)操作
这个操作代表的是打通一条根节点到现在节点的实链。
我们可以选择的方法是从下往上拉。
每次让要操作的节点成为当前\(\text{splay}\)的根,然后将它的右子树变成\(\text{0}\),接着在根据虚边往上拉。
你们可以画一棵树出来,更熟悉的理解一下。
\(\text{access}\)操作是\(\text{lct}\)的基础,其他操作全是在\(\text{access}\)的基础上完成的。
\(\text{makeroot}\)操作。
这个操作就是换根操作。
我们先要知道一点,那就是我们\(\text{access}\)后这个节点一定是当前\(\text{splay}\)中深度最大的节点。(应该容易理解,可以自行思考一下)。
此时我们再让此节点成为当前\(\text{splay}\)的根,那么这个节点一定是没有右子树的。
这个时候我们有一个神奇的思路:
直接把这个节点的左右子树交换,不就成了根了吗?
事实证明这种思路是可行的。
\(\text{findroot}\)操作
\(\text{x}\)所在的树中的根。
我们先\(\text{access}\)一次,然后再\(\text{splay}\)一次。
此时的所有点都在当前节点的左子树里,毫无疑问,原来的树根就是目前为止一直左子树到最后一个的点。
\(\text{split}\)操作
这个操作的含义定义为拉出一条从\(\text{x}\)\(\text{y}\)的链。
我们先\(\text{makeroot(x)}\),然后\(\text{access(y)}\),此时由于\(\text{x}\)是根节点,所以\(\text{x}\)\(\text{y}\)就处在了同一个\(\text{splay}\)中。
这时我们再将\(\text{y}\)变成现在\(\text{splay}\)里的根就好了。
\(\text{link}\)
操作
定义含义为连接某一条边。
我们发现\(\text{makeroot}\)之后由于是根节点,这个节点是没有父亲的,我们直接将这个节点的父亲赋为\(\text{y}\)就好了。
\(\text{cut}\)操作
定义为删掉\(\text{x}\)\(\text{y}\)的边
我们首先拉出一条从\(\text{x}\)\(\text{y}\)的链。
一般来说\(\text{cut}\)的时候一定是存在\(\text{x}\)\(\text{y}\)的边的,但是为了以防万一,这种特殊情况也要处理一下。
有几种情况:
1:\(\text{y}\)不仅有\(\text{x}\)一个儿子。
2:\(\text{x}\)的父亲不是\(\text{y}\)
3:\(\text{x}\)有右孩子。
然后你就可以写出\(\text{lct}\)了。
上代码:

// luogu-judger-enable-o2
#include <bits/stdc++.h>
#define il inline
const int maxn = 3e5 + 10; 
using namespace std;
template<class T> il void rd(T& res) {
    res = 0;char c;bool sign = 0;
    for(c = getchar();!isdigit(c);c = getchar()) sign |= c == '-';
    for(;isdigit(c);c = getchar()) res = (res << 1) + (res << 3) + (c ^ 48);    
    (sign) && (res = -res);
    return; 
} 
int n,m,i,j,k,root;     
int ch[maxn][2],size[maxn],fa[maxn],val[maxn],sum[maxn];         
bool tag[maxn],dbg;       
il bool isroot(int o) {
    return ch[fa[o]][0] != o && ch[fa[o]][1] != o;
}  
il void push_up(int o) {
    size[o] = size[ch[o][0]] + size[ch[o][1]] + 1;
    sum[o] = sum[ch[o][0]] ^ sum[ch[o][1]] ^ val[o];
    return;
}
il void _swap(int& x,int& y) {
    x ^= y ^= x ^= y;
    return; 
}
il void push_down(int o) {
    if(tag[o]) {
        _swap(ch[o][0],ch[o][1]); 
        tag[ch[o][0]] ^= 1;
        tag[ch[o][1]] ^= 1;
        tag[o] = 0;
    }
    return;
}
il int which(int o) {
    return ch[fa[o]][1] == o;
}
il void rotate(int o) {
    int f = fa[o],gf = fa[f],whi = which(o);    
    if(!isroot(f)) ch[gf][which(f)] = o;      
    ch[f][whi] = ch[o][whi ^ 1];
    fa[ch[o][whi ^ 1]] = f;         
    ch[o][whi ^ 1] = f;        
    fa[f] = o;fa[o] = gf;       
 	push_up(f);push_up(o);
    return;	
}            
il void splay(int o) {
    stack<int> sta;
    while(!sta.empty()) sta.pop();sta.push(o);            
    for(int i = o;!isroot(i);i = fa[i]) sta.push(fa[i]);             
    while(!sta.empty()) push_down(sta.top()),sta.pop();
    for(int f = fa[o];!isroot(o);rotate(o),f = fa[o]) {   
        if(!isroot(f)) rotate(which(f) == which(o) ? f : o);     
    //	if(!dbg) printf("%d\n",o); 
    }    
    push_up(o);
    return;
}                                 
il void access(int o) {        
    for(int y = 0;o;o = fa[y = o]) {           
        splay(o);      
        ch[o][1] = y;          
        push_up(o);
    }
}    
il int get_root(int o) {    
    access(o);splay(o);
    while(ch[o][0]) push_down(o),o = ch[o][0];    
    return o;
}
il void make_root(int o) {
    access(o);
    splay(o);          
    tag[o] ^= 1;
    return; 	
} 
il void link(int x,int y) {
    make_root(x);    
    fa[x] = y;
}
il void split(int x,int y) {
    make_root(x);
    access(y);
    splay(y);        
}
il void cut(int x,int y) {
    split(x,y);
    if(ch[y][which(x) ^ 1] || fa[x] != y || ch[x][1]) return;
    fa[x] = ch[y][0] = 0;
    push_up(y);
    return;
}
int main() {
    rd(n);rd(m);   
    for(int i = 1;i <= n;i++) rd(val[i]),sum[i] = val[i];
    for(int i = 1,op,x,y;i <= m;i++) {
        rd(op);rd(x);rd(y);     
        switch(op) {
            case 0: {
                split(x,y);   
                printf("%d\n",sum[y]);
                break;
            }
            case 1: {            
                if(get_root(x) != get_root(y)) link(x,y);
                break;
            }
            case 2: {
                if(get_root(x) == get_root(y)) cut(x,y);
                break;
            }
            case 3: {
                val[x] = y;
                access(x);
                splay(x);
                push_up(x);
                break;
            }
        }
    }
    return 0;
}




posted @ 2019-07-11 16:30  _connect  阅读(147)  评论(0)    收藏  举报
Live2D