[点分树] BZOJ 4372: 烁烁的游戏

Description

给一颗 n 个节点的树,边权均为1,初始点权均为 0 m次操作:
Q x :询问 x 的点权。
M x d w:将树上与节点 x 距离不超过d的节点的点权均加上 w

Solution

点分树大裸题。
先把点分树建出来,记录下分治结构上的祖先以及到该祖先的距离。
对每个点以深度为关键字建线段树,维护所管辖的分支结构的信息。
那么修改只要往分治树上爬,在线段树修改就好了。
当然要减掉从当前点走到分治树祖先又走回来的东西。
开两颗线段树就好了。
查询的话直接暴力爬分治树就好啦。

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int INF = 1 << 30;
const int B = 100;
const int N = 101010;
const int M = 30;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
template<typename T>
inline void read(T &x) {
    static char c; x = 0; int sgn = 0;
    for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1;
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
    if (sgn) x = -x;
}
inline void reado(int &opt) {
    static char c;
    for (c = get(); c != 'M' && c != 'Q'; c = get());
    opt = (c == 'M');
}

int n, m, x, y, z, Gcnt, Tcnt, opt;
struct edge {
    int to, next;
    edge (int t = 0, int n = 0):to(t), next(n) {}
};
edge G[N << 1];
int head[N];
int fa[N][M], dis[N][M];
int size[N], vis[N], mxd[N];
int root, mn, rt, sum, cnt;
int rt1[N], rt2[N], ls[N * B], rs[N * B], t[N * B];

inline void AddEdge(int from, int to) {
    G[++Gcnt] = edge(to, head[from]); head[from] = Gcnt;
    G[++Gcnt] = edge(from, head[to]); head[to] = Gcnt;
}
inline void Add(int &o, int l, int r, int L, int R, int x) {
    if (!o) o = ++Tcnt;
    if (l >= L && r <= R) return (void)(t[o] += x);
    int mid = (l + r) >> 1;
    if (L <= mid) Add(ls[o], l, mid, L, R, x);
    if (R > mid) Add(rs[o], mid + 1, r, L, R, x);
}
inline int Sum(int o, int l, int r, int pos) {
    if (l == r || !o) return t[o];
    int mid = (l + r) >> 1;
    if (pos <= mid) return t[o] + Sum(ls[o], l, mid, pos);
    else return t[o] + Sum(rs[o], mid + 1, r, pos);
}
inline void GetRoot(int u, int f) {
    int to, h = 0; size[u] = 1;
    for (int i = head[u]; i; i = G[i].next) {
        to = G[i].to; if (vis[to] || to == f) continue;
        GetRoot(to, u); size[u] += size[to];
        h = max(size[to], h);
    }
    h = max(sum - size[u], h);
    if (h < mn) {
        root = u; mn = h;
    }
}
inline void dfs(int u, int f, int d) {
    int to; ++cnt; mxd[rt] = max(mxd[rt], d);
    fa[u][++*fa[u]] = rt; dis[u][++*dis[u]] = d;
    for (int i = head[u]; i; i = G[i].next) {
        to = G[i].to; if (to == f || vis[to]) continue;
        dfs(to, u, d + 1);
    }
}
inline void DivAndConq(int u) {
    vis[u] = 1; int to; rt = u; mxd[rt] = 0;
    fa[u][++*fa[u]] = rt; dis[u][++*dis[u]] = 0;
    for (int i = head[u]; i; i = G[i].next) {
        to = G[i].to; if (vis[to]) continue;
        cnt = 0; dfs(to, u, 1); size[to] = cnt;
    }
    for (int i = head[u]; i; i = G[i].next) {
        to = G[i].to; if (vis[to]) continue;
        mn = INF; sum = size[to];
        GetRoot(to, u); DivAndConq(root);
    }
}
inline void Modify(int v, int k, int x) {
    int u = v, kk, fu;
    Add(rt1[u], 0, mxd[u], 0, min(k, mxd[u]), x);
    for (int i = 2; i <= *fa[v]; i++) {
        fu = fa[v][i]; kk = k - dis[v][i];
        if (kk < 0) {
            u = fu; continue;
        }
        Add(rt2[u], 0, mxd[fu], 0, min(kk, mxd[fu]), -x);
        u = fu;
        Add(rt1[u], 0, mxd[u], 0, min(kk, mxd[u]), x);
    }
}
inline int Query(int v) {
    int ans = 0, u = v, fu;
    ans += Sum(rt1[u], 0, mxd[u], 0);
    for (int i = 2; i <= *fa[v]; i++) {
        fu = fa[v][i];
        ans += Sum(rt2[u], 0, mxd[fu], dis[v][i]);
        u = fu;
        ans += Sum(rt1[u], 0, mxd[u], dis[v][i]);
    }
    return ans;
}
inline void Debug(void) {
    for (int i = 1; i <= n; i++)
        printf("w(%d)%d%c", i, Query(i), i == n ? '\n' : ' ');
}

int main(void) {
    freopen("1.in", "r", stdin);
    read(n); read(m);
    for (int i = 1; i < n; i++) {
        read(x); read(y);
        AddEdge(x, y);
    }
    mn = INF; sum = n;
    GetRoot(1, 0); DivAndConq(root);
    for (int i = 1; i <= n; i++) {
        reverse(fa[i] + 1, fa[i] + *fa[i] + 1);
        reverse(dis[i] + 1, dis[i] + *dis[i] + 1);
    }
    while (m--) {
        reado(opt); read(x);
        if (opt) {
            read(y); read(z);
            Modify(x, y, z);
        } else {
            printf("%d\n", Query(x));
        }
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值