CF1076E Vasya and a Tree(树状数组)
题目描述
Vasya 有一棵树,它有 n 个节点,1 号节点为根节点,初始所有点的权值为 0。
定义以下两个东西:
1 函数 (d(i,j))
指节点 i 到 j 所经过边的数量。
2 x 节点的 k 级子树
指满足以下条件点的集合:
① x 为该点的祖先,规定自己也是自己的祖先。
②(d(i,j) leq k)。
Vasya有 m 条要求要你来解决:
给出 v,d,x,将以 v 节点的 d 级子树的权值加上 x。
当处理完 Vasya 所有的要求时,输出所有点的权值。
数据范围
第 1 行包括一个整数,n((1 leq n leq 3 cdot 10^{5})),含义如上。
第 2 行到第 n 行,包括两个整数x和y((1 leq x, y leq n)),用空格隔开,表示两个点之间有一条边。
第 n+1 行有一个整数m((1 leq m leq 3 cdot 10^{5})),表示有m个要求。
第 n+2 行到第 n + m + 1行,有三个整数 v,d,x($ 1 leq v_{i} leq n , 0 leq d_{i} leq 10^{9} , 1 leq x_{i} leq 10^{9}$),用空格隔开,含义如上。
解题思路
解法一:离线+树状数组
将修改操作挂在 x 节点上,dfs 遍历整棵树,到 x 节点时将所有操作修改,就是将 (dep_x o dep_{x}+d_x) 都加上 (v_x),然后查询当前深度的权值,继续递归子树,回退时直接减去即可
#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MP make_pair
#define ll long long
#define fi first
#define se second
using namespace std;
template <typename T>
void read(T &x) {
x = 0; bool f = 0;
char c = getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
for (;isdigit(c);c=getchar()) x=x*10+(c^48);
if (f) x=-x;
}
template<typename F>
inline void write(F x)
{
static short st[30];short tp=0;
if(x<0) putchar('-'),x=-x;
do st[++tp]=x%10,x/=10; while(x);
while(tp) putchar('0'|st[tp--]);
putchar('
');
}
template <typename T>
inline void Mx(T &x, T y) { x < y && (x = y); }
template <typename T>
inline void Mn(T &x, T y) { x > y && (x = y); }
const int N = 400500;
int h[N], ne[N<<1], to[N<<1], tot, m, n;
inline void add(int x, int y) {
ne[++tot] = h[x], to[h[x] = tot] = y;
}
ll d[N], ans[N];
vector<pair<int, int> > v[N];
inline void Add(int x, int k) {
for (; x <= n; x += x & -x) d[x] += k;
}
inline ll sum(int x) {
ll res = 0;
for (; x; x -= x & -x) res += d[x];
return res;
}
void dfs(int x, int fa, int dep) {
for (auto g: v[x]) Add(dep, g.se), Add(dep + g.fi + 1, -g.se);
ans[x] = sum(dep);
for (int i = h[x]; i; i = ne[i]) {
int y = to[i]; if (y == fa) continue;
dfs(y, x, dep + 1);
}
for (auto g: v[x]) Add(dep, -g.se), Add(dep + g.fi + 1, g.se);
}
int main() {
read(n);
for (int i = 1, x, y;i < n; i++)
read(x), read(y), add(x, y), add(y, x);
read(m);
for (int i = 1, p, d, x;i <= m; i++)
read(p), read(d), read(x), v[p].push_back(MP(d, x));
dfs(1, 0, 1);
for (int i = 1;i <= n; i++) printf ("%lld ", ans[i]);
return 0;
}
解法二:维护矩形
容易发现将 (x o (dfn[x],dep[x])) 可以很好的表示此题中的关系,修改时首先在 x 的子树内可以用 dfs 序来解决,深度要求第二维解决,由此转化为若干矩阵相加,单点求值,本质和第一个方法一样
可以用树状数组或 kd-tree 维护,其中 kd-tree 可以做到在线,但复杂度不优且常数较大