[BZOJ4448][Scoi2015]情报传递(主席树)

看到询问,很容易想到可以使用树上主席树来解决。
但是修改如果直接做,则比较难实现,因为一个情报员搜集了情报之后,从第二天开始,这个情报员的危险值是持续增加的。但是,如果假设每个人的危险值每天都会加 1 ,这样就比较容易实现了。
所以,考虑离线。即存下所有的操作之后,扫一遍所有的搜集情报操作。如果第i个人在第 j 天搜集了情报,那么第i个人的初始危险值定为 qj+1 ,否则第 i 个人的初始危险值定为0。也就是说,如果第 i 个人在第j天搜集了情报,那么他在第 j 天的危险值为q+1(实际风险值为 0 )。简单的说,如果一个人记录下的初始危险值为x,那么这个人在第 i 天的实际危险值为
max(0,x+iq1)
而在第 i 天询问有多少个人的实际危险值大于c
就等价于询问有多少个人的初始危险值大于 q+ci+1
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 2e5 + 5, M = 4e5 + 5, NLogN = 1e7 + 5, LogN = 21;
int n, m, ecnt, nxt[M], adj[N], go[M], op[N], X[N], Y[N], C[N],
st[N], Orz, rt[N], dep[N], fa[N][LogN];
struct cyx {
    int lc, rc, cnt;
} T[NLogN];
void add_edge(int u, int v) {
    nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
    nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
}
void ins(int y, int &x, int l, int r, int k) {
    T[x = ++Orz] = T[y]; T[x].cnt++; if (l == r) return;
    int mid = l + r >> 1;
    if (k <= mid) ins(T[y].lc, T[x].lc, l, mid, k);
    else ins(T[y].rc, T[x].rc, mid + 1, r, k);
}
void dfs(int u, int fu) {
    ins(rt[fu], rt[u], 0, m, st[u]);
    int i; dep[u] = dep[fa[u][0] = fu] + 1;
    for (i = 0; i <= 18; i++) fa[u][i + 1] = fa[fa[u][i]][i];
    for (int e = adj[u], v; e; e = nxt[e]) {
        if ((v = go[e]) == fu) continue;
        dfs(v, u);
    }
}
int lca(int u, int v) {
    int i; if (dep[u] < dep[v]) swap(u, v);
    for (i = 19; i >= 0; i--) {
        if (dep[fa[u][i]] >= dep[v]) u = fa[u][i];
        if (u == v) return u;
    }
    for (i = 19; i >= 0; i--)
        if (fa[u][i] != fa[v][i])
            u = fa[u][i], v = fa[v][i];
    return fa[u][0];
}
int query(int p, int l, int r, int c) {
    if (l == r) return l > c; int mid = l + r >> 1;
    if (c <= mid) return query(T[p].lc, l, mid, c) + T[T[p].rc].cnt;
    else return query(T[p].rc, mid + 1, r, c);
}
int ask(int x, int y, int k) {
    int z = lca(x, y); printf("%d ", dep[x] + dep[y] - (dep[z] << 1) + 1);
    return query(rt[x], 0, m, k) + query(rt[y], 0, m, k)
        - query(rt[z], 0, m, k) - query(rt[fa[z][0]], 0, m, k);
}
int main() {
    int i, x, R; n = read();
    for (i = 1; i <= n; i++)
        if (x = read()) add_edge(i, x);
        else R = i;
    m = read(); for (i = 1; i <= m; i++) {
        op[i] = read(); if (op[i] == 1)
            X[i] = read(), Y[i] = read(), C[i] = read();
        else x = read(), st[x] = m - i + 1;
    }
    dfs(R, 0); for (i = 1; i <= m; i++) if (op[i] == 1)
        printf("%d\n", ask(X[i], Y[i], m + C[i] - i + 1));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值