CF1076E Vasya and a Tree(树状数组)

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 \to 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('\n');
}

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 \to (dfn[x],dep[x])\) 可以很好的表示此题中的关系,修改时首先在 x 的子树内可以用 dfs 序来解决,深度要求第二维解决,由此转化为若干矩阵相加,单点求值,本质和第一个方法一样

可以用树状数组或 kd-tree 维护,其中 kd-tree 可以做到在线,但复杂度不优且常数较大

posted @ 2020-06-03 21:32  Hs-black  阅读(179)  评论(0编辑  收藏  举报