1. 程式人生 > >[Libre][SDOI2017]樹點塗色

[Libre][SDOI2017]樹點塗色

hang tin += http != 表示 塗色 include 進行

鏈接

(蒟蒻只能夠想到 3 操作可以用樹剖線段樹維護ORZ)
對於每個 1 操作 , 都會覆蓋當前節點到根節點的所有顏色 , 且新顏色對所有經過操作節點的路徑都有貢獻。
所以我們維護所有邊的兩邊顏色是否相同 , 易知在不相同情況下這條邊有1的貢獻 , 在這種情況下我們就能夠對 2 操作進行維護了(考慮差分)

在這裏我們用 $ D_{i} $ 表示點 i 到根存在多少這樣的邊 ,則操作 2 的 答案為 $ Ans = D_{x} + D_{y} - 2 * D_{lca(x , y)} + 1 $

接下來考慮如何維護這些邊。

我們會發現覆蓋當前節點到根節點的操作與 LCT 中的 Access 操作類似 ,

所以在這裏我們就可以用輕重邊來表示這些不同的邊了!

用 Access 來模擬操作 1 過程 , 重邊 - > 輕邊 則進行子樹加 , 輕邊 - > 重邊 則進行子樹減

由於我們只要維護 LCT 中的 Access , 所以只要對Access操作進行一些修改就行了


#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;

const int N = 1e5 + 5;

struct Virt{
    double x , y;
    Virt(double _x = 0.0 , double _y = 0.0):x(_x) , y(_y){}
};

Virt operator + (Virt x , Virt y){return Virt(x.x + y.x , x.y + y.y);}
Virt operator - (Virt x , Virt y){return Virt(x.x - y.x , x.y - y.y);}
Virt operator * (Virt x , Virt y){return Virt(x.x * y.x - x.y * y.y , x.x * y.y + x.y * y.x);}

int read(){
    int x = 0 , f = 1; char ch = getchar();
    while(ch < ‘0‘ || ch > ‘9‘){if(ch == ‘-‘) f = -1 ; ch = getchar();}
    while(ch >= ‘0‘ && ch <= ‘9‘){x = x * 10 + ch - ‘0‘; ch = getchar();}
    return x * f;
}

int n , Q , opt , x , y;
int num , posin[N] , posout[N] , tot , head[N];
int fa[N] , dep[N] , sz[N] , top[N] , son[N] , tree[N];

struct edge{
    int pos , nx;
}e[N << 1];

void add(int u , int v){
    e[++num].pos = v; e[num].nx = head[u]; head[u] = num;
    e[++num].pos = u; e[num].nx = head[v]; head[v] = num;
}


void dfs1(int now){
    sz[now] = 1;
    for(int i = head[now] ; i ; i = e[i].nx){
        if(e[i].pos == fa[now]) continue;
        fa[e[i].pos] = now; dep[e[i].pos] = dep[now] + 1;
        dfs1(e[i].pos);
        sz[now] += sz[e[i].pos];
        if(sz[e[i].pos] > sz[son[now]]) son[now] = e[i].pos;
    }
}

void dfs2(int now , int father){
    posin[now] = ++tot; tree[tot] = now;
    top[now] = father;
    if(son[now]) dfs2(son[now] , father);
    for(int i = head[now] ; i ; i = e[i].nx){
        if(e[i].pos == son[now] || e[i].pos == fa[now]) continue;
        dfs2(e[i].pos , e[i].pos);
    }
    posout[now] = tot;
}

int lca(int x , int y){
    while(top[x] != top[y]){
        if(dep[top[x]] < dep[top[y]]) swap(x,y);
        x = fa[top[x]];
    }
    return dep[x] < dep[y] ? x : y;
}

struct SegmentTree{
    struct node{
        int tag , val;
    }tr[N << 2];

    void paint(int k , int d){
        tr[k].tag += d; tr[k].val+=d;
    }

    void push_down(int k){
        paint(k << 1 , tr[k].tag);
        paint(k << 1 | 1 , tr[k].tag);
        tr[k].tag = 0;
    }

    void merge(int k){tr[k].val = max(tr[k << 1].val , tr[k << 1 | 1].val);}

    void build(int k , int l , int r){
        if(l == r) tr[k].val = dep[tree[l]] + 1;
        else build(k << 1 , l , (l + r) >> 1) , build(k << 1 | 1 , ((l + r) >> 1) + 1 , r) , merge(k);
    }

    int query1(int k , int l , int r , int pos){
        if(l == r) return tr[k].val;
        else{
            if(tr[k].tag) push_down(k);
            int mid = (l + r) >> 1;
            if(mid >= pos) return query1(k << 1 , l , mid , pos);
            else return query1(k << 1 | 1, mid + 1 , r , pos);
        }
    }

    int query2(int k , int l , int r , int L , int R){
        if(L <= l && r <= R) return tr[k].val;
        else{
            if(tr[k].tag) push_down(k);
            int mid = (l + r) >> 1 , ans = 0;
            if(mid >= L) ans = max(ans , query2(k << 1 , l , mid , L , R));
            if(mid < R) ans = max(ans , query2(k << 1 | 1 , mid + 1 , r , L , R));
            return ans;
        }
    }

    void change(int k , int  l , int r , int L , int R , int d){
        if(l >= L && r <= R) paint(k , d);
        else{
            if(tr[k].tag) push_down(k);
            int mid = (l + r) >> 1;
            if(mid >= L) change(k << 1 , l , mid , L , R , d);
            if(mid < R) change(k << 1 | 1 , mid + 1 , r , L , R , d);
            merge(k);
        }
    }

}seg;

void addsub(int k , int d){seg.change(1 , 1 , n , posin[k] , posout[k] , d);}
int subquery(int x , int y){
    int temp = lca(x , y);
    //printf("lca : %d\n",temp);
    int ans = seg.query1(1 , 1 , n , posin[x]) + seg.query1(1 , 1 , n , posin[y]) - 2 * seg.query1(1 , 1 , n , posin[temp]) + 1;
    return ans;
}

int maxquery(int k){
    int ans = seg.query2(1 , 1 , n , posin[k] , posout[k]);
    return ans;
}

struct Link_Cut_Tree{
    struct node{int ch[2] , fa;}tr[N];

    int get_son(int x){return tr[tr[x].fa].ch[1] == x;}
    bool is_root(int x){return tr[tr[x].fa].ch[0] != x && tr[tr[x].fa].ch[1] != x;}

    void rotate(int k){
        int f = tr[k].fa , ffa = tr[f].fa , c = get_son(k);
        if(!is_root(f)) tr[ffa].ch[tr[ffa].ch[1] == f] = k; tr[k].fa = ffa;
        tr[f].ch[c] = tr[k].ch[!c]; tr[tr[f].ch[c]].fa = f;
        tr[k].ch[!c] = f; tr[f].fa = k;
    }   

    void splay(int x){
        for(int f ; !is_root(x) ; rotate(x))
            if(!is_root(f = tr[x].fa))
                rotate(get_son(x) == get_son(f) ? f : x);
    }

    int find_root(int x){
        splay(x);
        while(tr[x].ch[0]) x = tr[x].ch[0];
        splay(x);
        return x;
    }   

    void access(int x){
        for(int y = 0 ; x ; y = x , x = tr[x].fa){
            splay(x);
            if(tr[x].ch[1]){
                int temp = tr[x].ch[1]; tr[x].ch[1] = 0;
                temp = find_root(temp); addsub(temp , 1);
            }
            if(y) y = find_root(y) , addsub(y , -1);
            
            tr[x].ch[1] = y;
        }
    }

    void init(){for(int i = 1 ; i <= n ; i++) tr[i].fa = fa[i];};
}lct;

int main(){
    scanf("%d%d",&n,&Q);
    for(int i = 1 ; i < n ; i++) scanf("%d%d",&x,&y) , add(x , y);
    dfs1(1); dfs2(1 , 1); seg.build(1 , 1 , n); lct.init();
    for(int i = 1 ; i <= Q ; i++){
        scanf("%d%d",&opt,&x);
        if(opt == 1) lct.access(x);
        else if(opt == 2) scanf("%d",&y) , printf("%d\n" , subquery(x , y));
        else printf("%d\n" , maxquery(x)); 
    }
    return 0; 
}

[Libre][SDOI2017]樹點塗色