LOJ2001 SDOI2017 树点涂色 LCT、线段树

传送门


注意到每一次\(1\ x\)操作相当于一次LCT中的access操作。由LCT复杂度证明可以知道access的总次数不会超过\(O(nlogn)\),我们只需要模拟这个access的过程并在其中动态统计每一个点的答案。

我们考虑在虚实边切换的过程中即时更新答案。设当前即将把\(y \rightarrow x\)的虚边转换为实边,设此时\(x\)的实儿子为\(p\)。那么对于\(p\)及其子树,所有点到根的路径经过的颜色数量均\(+1\);对于\(y\)及其所有点的子树,它们经过的颜色的数量均\(-1\)。那么我们只需要实现子树加减,可以使用线段树。

对于\(3\)操作也就是一个子树\(\max\)操作,同样在线段树上维护;\(2\)操作可以知道\(val_{x,y} = val_{1,x} + val_{1,y} - 2val_{1,LCA(x,y)} + 1\),所以就是线段树上的单点查询。

#include<bits/stdc++.h>
using namespace std;

int read(){
    int a = 0; char c = getchar(); bool f = 0;
    while(!isdigit(c)){f = c == '-'; c = getchar();}
    while(isdigit(c)){
        a = a * 10 + c - 48; c = getchar();
    }
    return f ? -a : a;
}

const int _ = 1e5 + 7;
vector < int > ch[_];
int N , M , dep[_] , dfn[_] , sz[_] , ind[_] , ts , jump[_][20];

namespace segt{
    int mx[_ << 2] , mrk[_ << 2];

#define mid ((l + r) >> 1)
#define lch (x << 1)
#define rch (x << 1 | 1)
    
    void init(int x , int l , int r){
        if(l == r) mx[x] = dep[ind[l]];
        else{init(lch , l , mid); init(rch , mid + 1 , r); mx[x] = max(mx[lch] , mx[rch]);}
    }

    void mark(int x , int val){mrk[x] += val; mx[x] += val;}
    void down(int x){mark(lch , mrk[x]); mark(rch , mrk[x]); mrk[x] = 0;}
    
    void modify(int x , int l , int r , int L , int R , int val){
        if(l >= L && r <= R) return mark(x , val);
        down(x);
        if(mid >= L) modify(lch , l , mid , L , R , val);
        if(mid < R) modify(rch , mid + 1 , r , L , R , val);
        mx[x] = max(mx[lch] , mx[rch]);
    }

    int qry(int x , int l , int r , int L , int R){
        if(l >= L && r <= R) return mx[x];
        int mx = 0; down(x);
        if(mid >= L) mx = qry(lch , l , mid , L , R);
        if(mid < R) mx = max(mx , qry(rch , mid + 1 , r , L , R));
        return mx;
    }
#undef lch
#undef rch
}

namespace LCT{
    int fa[_] , ch[_][2];

    bool son(int x){return ch[fa[x]][1] == x;}
    bool nroot(int x){return ch[fa[x]][1] == x || ch[fa[x]][0] == x;}
    
    void rot(int x){
        bool f = son(x); int y = fa[x] , z = fa[y] , w = ch[x][f ^ 1];
        fa[x] = z; if(nroot(y)) ch[z][son(y)] = x;
        fa[y] = x; ch[x][f ^ 1] = y;
        ch[y][f] = w; if(w) fa[w] = y;
    }
    
    void splay(int x){while(nroot(x)){if(nroot(fa[x])) rot(son(fa[x]) == son(x) ? fa[x] : x); rot(x);}}
    
    void access(int x){
        for(int y = 0 ; x ; y = x , x = fa[x]){
            splay(x); 
            if(ch[x][1]){
                int t = ch[x][1]; while(ch[t][0]) t = ch[t][0];
                segt::modify(1 , 1 , N , dfn[t] , dfn[t] + sz[t] - 1 , 1);
            }
            if(y){
                int t = y; while(ch[t][0]) t = ch[t][0];
                segt::modify(1 , 1 , N , dfn[t] , dfn[t] + sz[t] - 1 , -1);
            }
            ch[x][1] = y; fa[y] = x;
        }
    }
}

void dfs(int x , int p){
    dep[x] = dep[p] + 1; ind[dfn[x] = ++ts] = x; sz[x] = 1;
    jump[x][0] = p; LCT::fa[x] = p;
    for(int i = 1 ; jump[x][i - 1] ; ++i) jump[x][i] = jump[jump[x][i - 1]][i - 1];
    for(auto t : ch[x]) if(t != p){dfs(t , x); sz[x] += sz[t];}
}

int LCA(int x , int y){
    if(dep[x] < dep[y]) swap(x , y);
    for(int i = 18 ; i >= 0 ; --i)
        if(dep[x] - (1 << i) >= dep[y]) x = jump[x][i];
    if(x == y) return x;
    for(int i = 18 ; i >= 0 ; --i)
        if(jump[x][i] != jump[y][i]){x = jump[x][i]; y = jump[y][i];}
    return jump[x][0];
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
    freopen("out","w",stdout);
#endif
    N = read(); M = read();
    for(int i = 1 ; i < N ; ++i){
        int a = read() , b = read();
        ch[a].push_back(b); ch[b].push_back(a);
    }
    dfs(1 , 0); segt::init(1 , 1 , N); int x , y , z;
    while(M--)
        switch(read()){
        case 1: LCT::access(read()); break;
        case 2:
            x = read(); y = read(); z = LCA(x , y);
            printf("%d\n" , segt::qry(1 , 1 , N , dfn[x] , dfn[x]) + segt::qry(1 , 1 , N , dfn[y] , dfn[y])
                   - 2 * segt::qry(1 , 1 , N , dfn[z] , dfn[z]) + 1); break;
        case 3: x = read(); printf("%d\n" , segt::qry(1 , 1 , N , dfn[x] , dfn[x] + sz[x] - 1));
        }
    return 0;
}

转载于:https://www.cnblogs.com/Itst/p/11509237.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值