洛谷P2146 [NOI2015]软件包管理器

题目链接

走这里

题目分析

对不起我就来水个博客
好像这段时间一直很颓,但是什么事情都不干又良心不安,所以就来颓个树剖。
说正事。
最开始做这个题的时候sb了一下,因为虽然可以当做染色染0和1,不知道怎么统计状态变化的软件包的数量,后来发现我们在线段树上维护的\(sum\)的变化值其实就是这段区间状态变化的软件包的数量,然后我们可以偷个懒不用区间查询,直接输出线段树根节点的\(sum\)变化量即可。

代码

贴一个极致压行的80行代码,第一次10min以内过树剖题,有点感动。

#include <bits/stdc++.h>
#define ls(p) p << 1
#define rs(p) p << 1 | 1 
#define N (200000 + 10)
using namespace std;
inline int read() {
    int cnt = 0, f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -f; c = getchar();}
    while (isdigit(c)) {cnt = (cnt << 3) + (cnt << 1) + c - '0'; c = getchar();}
    return cnt * f;
}
int nxt[N], to[N], first[N], tot, n, q, x;
void Add(int x, int y) {nxt[++tot] = first[x], first[x] = tot, to[tot] = y;}
int dep[N], siz[N], father[N], son[N], id[N], top[N], idx;
char ope[20];
void dfs_(int x, int fa) {
    dep[x] = dep[fa] + 1, siz[x] = 1, father[x] = fa;
    for (register int i = first[x]; i; i = nxt[i]) {
        int v = to[i];
        if (v == fa) continue;
        dfs_(v, x);
        siz[x] += siz[v];
        if (siz[son[x]] < siz[v]) son[x] = v;
    }
}
void dfs__(int x, int tp) {
    top[x] = tp, id[x] = ++idx;
    if (son[x]) dfs__(son[x], tp);
    for (register int i = first[x]; i; i = nxt[i]) {
        int v = to[i];
        if (!id[v]) dfs__(v, v);
    }
}
struct segmentree {
    int l, r, sum, tag;
    #define l(p) tree[p].l
    #define r(p) tree[p].r
    #define sum(p) tree[p].sum
    #define tag(p) tree[p].tag 
}tree[N << 2];
void push_up(int p) {sum(p) = sum(ls(p)) + sum(rs(p));}
void push_rev(int p, int k) {sum(p) = (k ? (r(p) - l(p) + 1) : 0), tag(p) = k;}
void push_down(int p) {
    if (tag(p) >= 0) push_rev(ls(p), tag(p)), push_rev(rs(p), tag(p)), tag(p) = -1;
}
void build(int p, int l, int r) {
    l(p) = l, r(p) = r;
    if (l == r) {tag(p) = -1; return;}
    int mid = (l + r) >> 1;
    build (ls(p), l, mid), build (rs(p), mid + 1, r);
    push_up(p);
}
void modify(int p, int l, int r, int d) {
    if (l <= l(p) && r >= r(p)) { push_rev(p, d); return;}
    push_down(p);
    int mid = (l(p) + r(p)) >> 1;
    if (l <= mid) modify (ls(p), l, r, d);
    if (r > mid) modify(rs(p), l, r, d);
    push_up(p); 
}
void Modify(int u, int v, int d) {
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        modify(1, id[top[u]], id[u], d);
        u = father[top[u]];
    }
    dep[u] < dep[v] ? modify(1, id[u], id[v], d) : modify(1, id[v], id[u], d);
}
signed main() {
    n = read(); 
    for (register int i = 2; i <= n; ++i) x = read() + 1, Add(x, i), Add(i, x);
    dfs_(1, 0), dfs__(1, 1), build (1, 1, n);
    q = read();
    for (register int i = 1; i <= q; i++) {
        scanf("%s", ope + 1); int ans = sum(1);
        x = read() + 1, ope[1] == 'i' ? Modify(x, 1, 1) : modify(1, id[x], id[x] + siz[x] - 1, 0); 
        printf("%d\n", abs(ans - sum(1)));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值