SPOJ QTREE6

题意

给你一棵 n n 个点的树,编号1~ n n 。每个点可以是黑色,可以是白色。初始时所有点都是黑色。有两种操作
0 u:询问有多少个节点 v v 满足路径u v v 上所有节点(包括)都拥有相同的颜色
1 u:翻转 u u 的颜色

Sol

LCT做法:

黑白开两个 LCT L C T
然后有一个很 Naive N a i v e 的做法
就是每次改颜色时,暴力枚举连的点, Link L i n k Cut C u t
显然菊花树直接就卡掉了

优化

把树定成一棵以 1 1 为根的有根树,然后把颜色放在它的父亲与它相连的边上
然后维护子树信息
Link Cut C u t 就只涉及一条边
注意到每个子树的根不一定同色
询问时要特判根的颜色
就是 Access A c c e s s 一下,然后 Splay S p l a y ,跳左儿子找到根,并把它 Splay S p l a y
如果同色,就输出它的子树信息,否则输出它的右儿子的子树信息
应为右儿子深度比它大,所以,右儿子的子树信息就是答案

由于我们定义它是有根树
那么我们不能 Makeroot M a k e r o o t ,这样的话才能保证能找到当前子树的根
那么 Link L i n k 时,为保证它父亲以上的点不受影响, Access A c c e s s Splay S p l a y 它父亲
又因为它就是当前的子树的根,把它 Splay S p l a y 一下,之后再连虚边
Cut C u t 时,把它 Access A c c e s s Splay S p l a y ,那么它的父亲就在左子树,直接删掉它和它左儿子的实边是没有影响的

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(1e5 + 5);

IL int Input(){
    RG int x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

struct LCT{
    typedef int Arr[_];
    Arr fa, ch[2], sum, val;

    IL int Son(RG int x){
        return ch[1][fa[x]] == x;
    }

    IL int Isroot(RG int x){
        return ch[0][fa[x]] != x && ch[1][fa[x]] != x;
    }

    IL void Update(RG int x){
        sum[x] = sum[ch[0][x]] + sum[ch[1][x]] + val[x] + 1;
    }

    IL void Rotate(RG int x){
        RG int y = fa[x], z = fa[y], c = Son(x);
        if(!Isroot(y)) ch[Son(y)][z] = x; fa[x] = z;
        ch[c][y] = ch[!c][x], fa[ch[c][y]] = y;
        ch[!c][x] = y, fa[y] = x, Update(y);
    }

    IL void Splay(RG int x){
        for(RG int y = fa[x]; !Isroot(x); Rotate(x), y = fa[x])
            if(!Isroot(y)) Son(x) ^ Son(y) ? Rotate(x) : Rotate(y);
        Update(x);
    }

    IL void Access(RG int x){
        for(RG int y = 0; x; y = x, x = fa[x]){
            Splay(x), val[x] += sum[ch[1][x]] - sum[y];
            ch[1][x] = y, Update(x);
        }
    }

    IL int Findroot(RG int x){
        Access(x), Splay(x);
        while(ch[0][x]) x = ch[0][x];
        Splay(x);
        return x;
    }

    IL void Link(RG int x, RG int y){
        if(!y) return;
        Access(y), Splay(x), Splay(y);
        fa[x] = y, val[y] += sum[x], Update(y);
    }

    IL void Cut(RG int x, RG int y){
        if(!y) return;
        Access(x), Splay(x);
        ch[0][x] = fa[ch[0][x]] = 0, Update(x);
    }
} T[2];
int fa[_], n, m, col[_];
vector <int> G[_];

IL void Dfs(RG int u, RG int ff){
    for(RG int i = 0, l = G[u].size(); i < l; ++i){
        RG int v = G[u][i];
        if(v == ff) continue;
        T[1].Link(v, u), fa[v] = u;
        Dfs(v, u);
    }
}

int main(RG int argc, RG char *argv[]){
    n = Input();
    for(RG int i = 1; i <= n; ++i) col[i] = 1;
    for(RG int i = 1; i < n; ++i){
        RG int u = Input(), v = Input();
        G[u].push_back(v), G[v].push_back(u);
    }
    Dfs(1, 0), m = Input();
    for(RG int i = 1; i <= m; ++i){
        RG int op = Input(), x = Input(), ff, &c = col[x];
        if(op) T[c].Cut(x, fa[x]), c ^= 1, T[c].Link(x, fa[x]);
        else{
            T[c].Access(x), ff = T[c].Findroot(x);
            if(col[ff] == c) printf("%d\n", T[c].sum[ff]);
            else printf("%d\n", T[c].sum[T[c].ch[1][ff]]);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值