洛谷 P3690 【模板】Link Cut Tree (動態樹)

題目鏈接:https://www.luogu.org/problem/P3690

動態樹:動態樹是一種超強級數據結構,它比樹鏈剖分更加強大,它能完成樹鏈剖分的基本操作,還能刪邊,連邊。

               它與樹鏈剖分的不同點在於:它的鏈是可變的,分實鏈和虛鏈,因爲更加靈活,所以採用了splay進行維護

               想學會動態樹,需要前置技能有:樹鏈剖分,splay樹,文藝平衡樹。

               抽象理解動態樹:可以簡單理解爲每一條鏈實鏈都是一顆splay,有一條主實鏈從某一個點連到根上,其他的都是副實鏈,這些副實鏈構成的splay樹的父親指向主實鏈上的splay樹對應的自己的父親節點(主實鏈和副實鏈是自己口胡的,只是爲了方便理解)(副實鏈上面還能再跟子副鏈......不用管,假裝它不存在,比較好理解)

樹鏈剖分是用線段樹來維護重鏈,其實可以理解爲把每段重鏈都分成了一顆線段樹,和動態樹把每一段實鏈都分成了一顆splay的道理是一樣的。

好了,我吹不下去了,知識有限,請觀摩大犇博客:https://www.cnblogs.com/flashhu/p/8324551.html

附上代碼:(正常沒數組版快,但開O2優化後差不多,也不知道是不是結構體複雜度比較高的問題)

/*
洛谷 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。
n和m,代表點數和操作數。
第2行到第n+1行,每行一個整數,代表每個點的權值。
第n+2行到第n+m+1行,每行三個整數,分別代表操作類型和操作所需的量。
對於每一個0號操作,你須輸出x到y的路徑上點權的xor和。

3 3 
1
2
3
1 1 2
0 1 2 
0 1 1
ans:
3
1
*/
#include<bits/stdc++.h>
#define lc c[x][0]
#define rc c[x][1]
#pragma GCC optimize(2)
#define ls(x) T[x].ch[0]
#define rs(x) T[x].ch[1]
#define M 100010
#define kind(x) (rs(T[x].f)==x)
using namespace std;
struct node
{
    int ch[2],f,v,s,tag;
} T[M];
int n,m,st[M];
void downing(int x)
{
    swap(ls(x),rs(x));
    T[x].tag^=1;
}
void down(int x)
{
    if(T[x].tag)
    {
        if(ls(x)) downing(ls(x));
        if(rs(x)) downing(rs(x));
        T[x].tag=0;
    }
}
void up(int x)
{
    T[x].s = T[ls(x)].s ^ T[rs(x)].s ^ T[x].v;
}
int nroot(int x)//判斷x是不是splay的根
{
    return ls(T[x].f)==x||rs(T[x].f)==x;
}
void rol(int x)
{
    int fa=T[x].f,ffa=T[fa].f;
    int k=kind(x),w=T[x].ch[!k];
    T[fa].ch[k]=w;
    T[x].ch[!k]=fa;
    if(nroot(fa))T[ffa].ch[kind(fa)]=x;
    T[x].f=ffa;
    T[fa].f=x;
    if(w)T[w].f=fa;
    up(x),up(fa);
}
void splay(int x)
{
    int y=x,z=0;
    st[++z]=y;
    while(nroot(y)) st[++z]=y=T[y].f;
    while(z>0) down(st[z--]);
    while(nroot(x))
    {
        int f=T[x].f,ff=T[f].f;
        if(nroot(f))
        {
            kind(x)^kind(f)?rol(f):rol(x);
        }
        rol(x);
    }
    up(x);
}
void access(int x)//把x到樹根的路徑拉爲實邊
{
    for(int y=0; x; x=T[y=x].f)
        splay(x),rs(x)=y,up(x);
}
void makeroot(int x)//把x變爲樹的根
{
    access(x);
    splay(x);
    downing(x);
}
int query(int x,int y)
{
    makeroot(x);
    access(y);
    splay(y);
    return T[y].s;
}
int findroot(int x)
{
    access(x);
    splay(x);
    while(ls(x))down(x),x=ls(x);
    splay(x);
    return x;
}
void link(int x,int y)
{
    makeroot(x);
    if(findroot(y)!=x)
    {
        T[x].f=y;
    }
}
void cut(int x,int y)
{
    makeroot(x);
    if(findroot(y)==x&&T[y].f==x&&!T[y].ch[0])
    {
        T[x].ch[1]=T[y].f=0;
        up(x);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++) scanf("%d",&T[i].v);
    int opt,x,y;
    while(m--)
    {
        scanf("%d%d%d",&opt,&x,&y);
        if(opt==0)
            printf("%d\n",query(x,y));
        if(opt==1)
            link(x,y);
        if(opt==2)
            cut(x,y);
        if(opt==3)
            splay(x),T[x].v=y;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章