SP16549 QTREE6 - Query on a tree VI LCT维护颜色联通块

\(\color{#0066ff}{ 题目描述 }\)

给你一棵n个点的树,编号1~n。每个点可以是黑色,可以是白色。初始时所有点都是黑色。下面有两种操作请你操作给我们看:

0 u:询问有多少个节点v满足路径u到v上所有节点(包括)都拥有相同的颜色
1 u:翻转u的颜色

\(\color{#0066ff}{输入格式}\)

一行一个整数n

接下来n-1行,每行两个整数表示一条边

接下来一行一个整数m表示操作次数

接下来m行,每行两个整数分别表示操作类型和被操作节点

\(\color{#0066ff}{输出格式}\)

对每个询问操作输出相应的结果

\(\color{#0066ff}{输入样例}\)

5
1 2
1 3
1 4
1 5
3
0 1
1 1
0 1

\(\color{#0066ff}{输出样例}\)

5
1

\(\color{#0066ff}{数据范围与提示}\)

对所有数据,\(1\leq n \leq 10^5\)\(1\leq m \leq 10^5\)

\(\color{#0066ff}{ 题解 }\)

容易想到,开两个LCT分别维护黑白两种颜色
然后修改颜色的时候,暴力cut相邻的边,link相邻的边
然而。。。存在一种东东叫菊花图。。。
显然很容易就被Hack掉了
正常LCT的题目中,如果有边权,我们肯定是边权转到点权上,这样才好维护
然而。。。本题就是与众不同,他要把点权转到边权上!
把每个点的点权转到它与父亲的边上,特别的,开一个0号节点,\(0-1\)存的是1的点权
然后询问的联通块即为所在联通块根的siz-1,注意,根的颜色跟其它点不同
因为根父亲的边不在这里!
因此仍然是LCT维护子树大小即可
对于改变颜色的问题,发现只需要断一条边即可
所以复杂度就保证了
#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
const int maxn = 1e5 + 10;
struct LCT {
protected:
    struct node {
        node *ch[2], *fa;
        int siz, tot;
        node(int siz = 0, int tot = 1): siz(siz), tot(tot) { ch[0] = ch[1] = fa = NULL; }
        void upd() {
            tot = siz + 1;
            if(ch[0]) tot += ch[0]->tot;
            if(ch[1]) tot += ch[1]->tot;
        }
        bool ntr() { return fa && (fa->ch[0] == this || fa->ch[1] == this); }
        bool isr() { return fa->ch[1] == this; }
    }pool[maxn];
    void rot(node *x) {
        node *y = x->fa, *z = y->fa;
        bool k = x->isr(); node *w = x->ch[!k];
        if(y->ntr()) z->ch[y->isr()] = x;
        (x->ch[!k] = y)->ch[k] = w;
        (y->fa = x)->fa = z;
        if(w) w->fa = y;
        y->upd(), x->upd();
    }
    void splay(node *o) {
        while(o->ntr()) {
            if(o->fa->ntr()) rot(o->isr() ^ o->fa->isr()? o : o->fa);
            rot(o);
        }
    }
    void access(node *x) {
        for(node *y = NULL; x; x = (y = x)->fa) {
            splay(x);
            if(x->ch[1]) x->siz += x->ch[1]->tot;
            if((x->ch[1] = y)) x->siz -= y->tot;
            x->upd();
        }
    }
    node *findroot(node *o) {
        access(o), splay(o);
        while(o->ch[0]) o = o->ch[0];
        return splay(o), o;
    }
public:
    void link(int l, int r) {
        node *x = pool + l, *y = pool + r;
        access(x), splay(x);
        x->fa = y;
        access(y), splay(y);
        y->siz += x->tot;
        y->upd();
    }
    void cut(int l, int r) {
        node *x = pool + l;
        access(x), splay(x);
        x->ch[0] = x->ch[0]->fa = NULL;
        x->upd();
    }
    int query(int p) {
        node *o = findroot(pool + p);
        return o->ch[1]->tot;
    }
}s[2];
struct node {
    int to;
    node *nxt;
    node(int to = 0, node *nxt = NULL): to(to), nxt(nxt) {}
};
node *head[maxn];
int n, m, f[maxn], col[maxn];
void add(int from, int to) {
    head[from] = new node(to, head[from]); 
}
void dfs(int x, int fa) {
    f[x] = fa;
    s[1].link(x, fa);
    for(node *i = head[x]; i; i = i->nxt) 
        if(i->to != fa) 
            dfs(i->to, x);
}
int main() {
    n = in();
    int x, y;
    for(int i = 1; i <= n; i++) col[i] = 1;
    for(int i = 1; i < n; i++) x = in(), y = in(), add(x, y), add(y, x);
    dfs(1, 0);
    for(m = in(); m --> 0;) {
        x = in(), y = in(); 
        if(x == 0) printf("%d\n", s[col[y]].query(y));
        if(x == 1) s[col[y]].cut(y, f[y]), s[col[y] ^= 1].link(y, f[y]);
    }
    return 0;
}

转载于:https://www.cnblogs.com/olinr/p/10397235.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值