Description
给一颗
n
个节点的树,边权均为
Q x
:询问
x
的点权。
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;
}