[CF1017G]The Tree[树链剖分+线段树]

题意

给一棵一开始 \(n\) 个点全是白色的树,以 \(1\) 为根,支持三种操作:

1.将某一个点变黑,如果已经是黑色则该操作对所有儿子生效。
2.将一棵子树改成白色。
3.询问某个点的颜色。

\(n\leq 10^5\)

分析

  • 唯一棘手的问题是操作1,我们很难正面解决这个问题。

  • 考虑点 \(u\) 到根的路径,只有在这些点上的操作才可能影响 \(u\) 的颜色。
    \(u\) 变黑的那次操作一定是先选定一个 \(u\) 的祖先,然后祖先到 \(u\) 的路径上所有的点都已经是黑色。

  • 先将所有点都标记成 \(-1\) ,表示要将其变黑需要一步,1操作就在对应节点+1。
    所以如果一个点是黑色当且仅当他到根路径上存在一个后缀和 \(\geq 0\)

  • 操作2在将子树变白之后还要考虑子树祖先的影响,可以理解成之前下放的标记废掉了,子树的根节点减去 在根节点查询到的后缀最大值+1 即可。之后如果子树内的点查询时的答案在子树祖先上,那一部分贡献就被删除了。

  • 有没有可能子树的祖先信息修改了,而当前点开始抵消的部分没有撤销?因为覆盖都是子树修改,如果祖先改了撤销信息也没有了,所以不用考虑。

  • 总时间复杂度为 \(O(nlog^2n)\)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define go(u) for(int i = head[u], v = e[i].to; i; i=e[i].lst, v=e[i].to)
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define pb push_back
inline int gi() {
    int x = 0,f = 1;
    char ch = getchar();
    while(!isdigit(ch)) {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(isdigit(ch)) {
        x = (x << 3) + (x << 1) + ch - 48;
        ch = getchar();
    }
    return x * f;
}
template <typename T> inline void Max(T &a, T b){if(a < b) a = b;}
template <typename T> inline void Min(T &a, T b){if(a > b) a = b;}
const int N = 1e5 + 7, inf = 0x3f3f3f3f;
int n, q, edc, tim;
int head[N], fa[N], zson[N], son[N], top[N], in[N], out[N];
struct edge {
    int lst, to;
    edge(){}edge(int lst, int to):lst(lst), to(to){}
}e[N*2];
void Add(int a, int b) {
    e[++edc] = edge(head[a], b), head[a] = edc;
    e[++edc] = edge(head[b], a), head[b] = edc;
}
void dfs1(int u) {
    son[u] = 1;
    go(u)if(v ^ fa[u]) {
        fa[v] = u, dfs1(v);
        son[u] += son[v];
        if(son[v] > son[zson[u]]) zson[u] = v;
    }
}
void dfs2(int u,int from) {
    in[u] = ++tim; top[u] = from;
    if(zson[u]) dfs2(zson[u], from);
    go(u)if(v ^ fa[u] && v ^ zson[u]) 
        dfs2(v, v);
    out[u] = tim;
}
int setv[N << 2];
#define Ls o << 1
#define Rs o << 1 | 1
struct data {
    int mx,s;
    data(){mx = -inf;}data(int mx, int s):mx(mx), s(s) {}
    data operator +(const data &b) const {
        return data(max(b.mx, mx + b.s), s + b.s);
    }
}t[N << 2];
void md(int l, int r, int o) {
    setv[o] = 1;
    t[o].mx = -1, t[o].s = -(r - l + 1);
}
void pushdown(int l, int r, int o) {
    if(!setv[o]) return;
    int mid = l + r >> 1;
    md(l, mid, Ls);
    md(mid + 1, r, Rs);
    setv[o] = 0;
}
void pushup(int o) {
    t[o] = t[Ls] + t[Rs];
}
void build(int l, int r, int o) {
    if(l == r) {
        t[o].mx = t[o].s = -1;
        return;
    }int mid = l + r >> 1;
    build(l, mid, Ls);
    build(mid + 1, r, Rs);
    pushup(o);
}
void m1(int p, int l, int r, int o, int v) {
    if(l == r) {
        t[o].mx += v, t[o].s += v;
        return;
    }
    pushdown(l, r, o);int mid = l + r >> 1;
    if(p <= mid) m1(p, l, mid, Ls, v);
    else m1(p, mid + 1, r ,Rs, v);
    pushup(o);
}
void m2(int L, int R, int l, int r, int o) {
    if(L <= l && r <= R) {
        md(l, r, o);
        return;
    }
    pushdown(l, r, o);int mid = l + r >> 1;
    if(L <= mid) m2(L, R, l, mid, Ls);
    if(R > mid)  m2(L, R, mid + 1, r, Rs);
    pushup(o);
}
data query(int L, int R, int l, int r, int o) {
    if(L <= l && r <= R) return t[o];
    pushdown(l, r, o); int mid = l + r >> 1;
    if(R <= mid)  return query(L, R, l, mid, Ls);
    if(L > mid) return query(L, R, mid + 1, r, Rs);
    return query(L, R, l, mid, Ls) + query(L, R, mid + 1, r, Rs);
}
int qry(int u) {
    data res(-inf, 0);
    for(; u; u = fa[top[u]]) {
        res = query(in[top[u]], in[u], 1, n, 1) + res;
    }
    return res.mx;
}
int main() {
    n = gi(), q = gi();
    rep(i, 2, n) Add(gi(), i);
    dfs1(1); dfs2(1, 1);
    build(1, n, 1);
    while(q--) {
        int opt = gi(), u = gi();
        if(opt == 1) {
            m1(in[u], 1, n, 1, 1);
        }else if(opt == 2){
            int t = qry(u);
            m1(in[u], 1, n, 1, -(t + 1));
            if(in[u] < out[u])
            m2(in[u] + 1, out[u], 1, n, 1);
        }else {
            int t = qry(u);
            puts(t >= 0 ? "black" : "white");
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/yqgAKIOI/p/10114382.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值