COGS-2295 榴莲(整体二分+树状数组+树上差分)

2295. [HZOI 2015]榴莲

★★☆   输入文件: Durio_zibethinus_Murr.in   输出文件: Durio_zibethinus_Murr.out    简单对比
时间限制:1 s   内存限制:512 MB

【题目描述】

众所周知,榴莲长在树上,同时榴莲很臭

现在给定你一棵榴莲树,树根为1

要求你维护以下操作:

1、 1 u v w 树上长出一个榴莲,臭味度为w,臭味弥漫了树上u到v的简单路径上所有点

2、 2 u w 树上长出一个榴莲,臭味度为w,臭味弥漫了树上u的子树的所有点

3、 3 k  树上k时刻长出的榴莲从树上掉了下来,它所造成的臭味效果消失

4、 4 u k 询问所有弥漫到u节点的榴莲臭味值的第k大

【输入格式】

第一行输入n,m表示节点总数和操作次数

以下n-1行u,v描述两个端点

以下m行,第i行表示第i个时刻的操作如题意所示

n,m<=100000

数据保证第三个操作掉下来的是存在的且尚未掉下来的榴莲

【输出格式】

对于每个询问

若弥漫到u节点的榴莲不足k个,则输出-1

否则输出对应的答案

【样例输入】

10 10

2 1

3 1

4 3

5 4

6 1

7 3

8 3

9 7

10 2

4 1 1

1 3 1 13881

3 2

2 10 12573

2 7 2522

1 10 7 7976

1 7 7 15696

4 2 1

1 3 4 14384

1 10 9 7398

【样例输出】

-1

7976

题解:整体二分+树状数组+树上差分

首先预处理出DFS序和LCA,设in[u]和out[u]是以u为根节点的子树的最小和最大的DFS序

对于修改操作,用树状数组维护贡献:

①链上操作(X,Y):

这个操作等价于

a. 对X到根节点路径上所有点贡献+1

b. 对Y到根节点路径上所有点贡献+1

c. 对LCA(X, Y)到根节点路径上所有点贡献-1

d. 对LCA(X,Y)的父节点 par[LCA(X, Y)]到根节点路径上所有点贡献-1

②子树操作(X):这个操作等价于将DFS序为in[u]~out[u]之间的点贡献+1

我们可以二分答案m,之后扫一遍之前的操作我们只需要知道有多少个权值>=m的操作经过当前点u就可以了,如果k不超过在这个节点的贡献cnt,则放入右区间,否则将k-=cnt并放入左区间。最后判断一下k是否小于或等于0,是的话答案为m,否则为-1(没有k个臭味)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MX = 1e5 + 5;
const LL inf = 0x3f3f3f3f3f3f3f3f;
struct Tree {
    int n;
    vector <int> T;
    void init (int _n) {
        T.clear();
        n = _n;
        T.resize(n + 1);
    }
    void add (int x, int v) {
        for (int i = x; i <= n; i += i & -i) T[i] += v;
    }
    int sum (int x) {
        if (x > n) x = n;
        int ret = 0;
        for (int i = x; i > 0; i -= i & -i) ret += T[i];
        return ret;
    }
} bt1, bt2;
struct Query {
    int op, u, v, w, k, id;
} que[MX], t1[MX], t2[MX];
struct Edge {
    int v, nxt;
} E[MX * 2];
int head[MX], tot, ans[MX], h[MX], mark[MX];
void add(int u, int v) {
    E[tot].v = v;
    E[tot].nxt = head[u];
    head[u] = tot++;
}
//ver:节点编号 deep:深度 first:点编号位置 dir:距离
int sz, ver[2 * MX], deep[2 * MX], first[MX], dp[2 * MX][30], vis[MX], dir[MX];
int rear, seq[2 * MX], in[MX], out[MX], pre[MX];
void init(int n) {
    for (int i = 0; i <= n; i++) dir[i] = vis[i] = 0;
    sz = 0;
}
void dfs(int u, int dep, int fa) {
    vis[u] = true; ver[++sz] = u;
    first[u] = sz; deep[sz] = dep;
    seq[++rear] = u; in[u] = rear; pre[u] = fa;
    for (int i = head[u]; ~i ; i = E[i].nxt) {
        if ( vis[E[i].v] ) continue;
        int v = E[i].v;
        dir[v] = dir[u] + 1;
        dfs(v, dep + 1, u);
        ver[++sz] = u; deep[sz] = dep;
    }
    seq[++rear] = u;
    out[u] = rear;
}
void ST(int n) {
    for (int i = 1; i <= n; i++) dp[i][0] = i;
    for (int j = 1; (1 << j) <= n; j++) {
        for (int i = 1; i + (1 << j) - 1 <= n; i++) {
            int a = dp[i][j - 1], b = dp[i + (1 << (j - 1))][j - 1];
            dp[i][j] = deep[a] < deep[b] ? a : b;
        }
    }
}
int RMQ(int l, int r) {
    int k = 0;
    while ((1 << (k + 1)) <= r - l + 1) k++;
    int a = dp[l][k], b = dp[r - (1 << k) + 1][k]; //保存的是编号
    return deep[a] < deep[b] ? a : b;
}
int LCA(int u, int v) {
    int x = first[u], y = first[v];
    if (x > y) swap(x, y);
    int ret = RMQ(x, y);
    return ver[ret];
}
void pre_solve(int n) {
    dir[1] = 1;
    dfs(1, 1, 0);
    ST(sz);
}
inline void update1(int u, int v, int lca, int val) {
    bt1.add(in[u], val); bt1.add(in[v], val); bt1.add(in[lca], -val);
    if (pre[lca]) bt1.add(in[pre[lca]], -val);
}
inline void update2(int u, int val) {
    bt2.add(in[u], val); bt2.add(out[u], -val);
}
void work1(int L, int R, int m) {
    for (int i = L, u, v, lca; i <= R; i++) {
        if (que[i].op == 1) {
            if (que[i].w == m) {
                update1(que[i].u, que[i].v, LCA(que[i].u, que[i].v), 1);
                mark[que[i].id] = i;
            }
        }
        if (que[i].op == 2) {
            if (que[i].w == m) {
                update2(que[i].u, 1);
                mark[que[i].id] = i;
            }
        }
        if (que[i].op == 3) {
            if (mark[que[i].k]) {
                int j = mark[que[i].k]; mark[que[j].id] = 0;
                if (que[j].op == 1) update1(que[j].u, que[j].v, LCA(que[j].u, que[j].v), -1);
                else update2(que[j].u, -1);
            }
        }
        if (que[i].op == 4) {
            u = que[i].u;
            int sum = bt1.sum(out[u]) - bt1.sum(in[u] - 1) + bt2.sum(in[u]);
            que[i].k -= sum;
        }
    }
    for (int i = L; i <= R; i++) {
        if (mark[que[i].id]) {
            if (que[i].op == 1) update1(que[i].u, que[i].v, LCA(que[i].u, que[i].v), -1);
            else update2(que[i].u, -1);
        }
        mark[que[i].id] = 0;
    }
}
void work2(int L, int R, int m, int &p1, int &p2) {
    p1 = 0, p2 = 0;
    for (int i = L, u, v, lca; i <= R; i++) {
        if (que[i].op == 1) {
            if (que[i].w < m) t1[p1++] = que[i];
            else {
                update1(que[i].u, que[i].v, LCA(que[i].u, que[i].v), 1);
                t2[p2++] = que[i], mark[que[i].id] = i;
            }
        }
        if (que[i].op == 2) {
            if (que[i].w < m) t1[p1++] = que[i];
            else {
                update2(que[i].u, 1);
                t2[p2++] = que[i], mark[que[i].id] = i;
            }
        }
        if (que[i].op == 3) {
            if (!mark[que[i].k]) t1[p1++] = que[i];
            else {
                int j = mark[que[i].k]; mark[que[i].k] = 0;
                if (que[j].op == 1) update1(que[j].u, que[j].v, LCA(que[j].u, que[j].v), -1);
                else update2(que[j].u, -1);
                t2[p2++] = que[i];
            }
        }
        if (que[i].op == 4) {
            u = que[i].u;
            int sum = bt1.sum(out[u]) - bt1.sum(in[u] - 1) + bt2.sum(in[u]);
            if (sum < que[i].k) que[i].k -= sum, t1[p1++] = que[i];
            else t2[p2++] = que[i];
        }
    }
    for (int i = 0; i < p1; i++) que[L + i] = t1[i], mark[t1[i].id] = 0;
    for (int i = 0; i < p2; i++) {
        if (mark[t2[i].id]) {
            if (t2[i].op == 1) update1(t2[i].u, t2[i].v, LCA(t2[i].u, t2[i].v), -1);
            else update2(t2[i].u, -1);
        }
        que[L + p1 + i] = t2[i], mark[t2[i].id] = 0;
    }
}
void solve(int L, int R, int l, int r) {
    if (L > R || l > r) return;
    if (l == r) {
        work1(L, R, l);
        for (int i = L; i <= R; i++) {
            if (que[i].op != 4) continue;
            if (que[i].k <= 0) ans[que[i].id] = h[l];
            else ans[que[i].id] = -1;
        }
        return;
    }
    int m = (l + r + 1) >> 1, p1, p2;
    work2(L, R, m, p1, p2);
    solve(L, L + p1 - 1, l, m - 1);
    solve(L + p1, R, m, r);
}
int main() {
    //freopen("in.txt", "r", stdin);
    freopen("Durio_zibethinus_Murr.in", "r", stdin);
    freopen("Durio_zibethinus_Murr.out", "w+", stdout);
    int n, m;
    scanf("%d%d", &n, &m);
    memset(head, -1, sizeof(head));
    for (int i = 1, u, v; i < n; i++) scanf("%d%d", &u, &v), add(u, v), add(v, u);
    pre_solve(n);
    bt1.init(rear); bt2.init(rear);
    int mx = 0, cnt = 0;
    for (int i = 1; i <= m; i++) {
        scanf("%d", &que[i].op);
        if (que[i].op == 1) scanf("%d%d%d", &que[i].u, &que[i].v, &que[i].w), h[++cnt] = que[i].w, mx = max(que[i].w, mx);
        if (que[i].op == 2) scanf("%d%d", &que[i].u, &que[i].w), h[++cnt] = que[i].w, mx = max(que[i].w, mx);
        if (que[i].op == 3) scanf("%d", &que[i].k);
        if (que[i].op == 4) scanf("%d%d", &que[i].u, &que[i].k);
        que[i].id = i; ans[i] = -2;
    }
    sort(h + 1, h + cnt + 1);
    cnt = unique(h + 1, h + cnt + 1) - h - 1;
    for (int i = 1; i <= m; i++) if (que[i].op == 1 || que[i].op == 2) que[i].w = lower_bound(h + 1, h + cnt + 1, que[i].w) - h;
    solve(1, m, 0, mx);
    for (int i = 1; i <= m; i++) if (ans[i] >= -1)printf("%d\n", ans[i]);
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值