题目:戳这里
题意:给定有n个点的一棵树,顶点1为根。m次操作,每次都把以v为根,深度dep以内的子树中所有的顶点(包括v本身)加x。求出最后每个点的值为多少。
解题思路:考虑到每次都只对点及其子树操作,要用dfs。设v当前要操作的点,操作的深度是dep,d[v]表示v的深度。要把深度[d[v],d[v]+dep]中所有点都加上x,暴力加是肯定不行的,于是想到要用树状数组或线段树。dfs+树状数组便是本题的基本思路。我们在搜索树的同时,维护以深度为下标的树状数组。为什么一个树形结构能够维护树状数组这样的线性结构呢?因为是对树的dfs,只要没有跳出点就一定搜一条线到底,这样搜索出来的点就能满足树状数组的线性。而每当跳出一个点,就把这个点给树状数组加的值减掉(回溯)即可。
附本人代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 #include <vector> 5 #define lowbit(x) x&-x 6 typedef long long ll; 7 const int maxn = 3e5+10; 8 const ll inf = 1e18; 9 using namespace std; 10 ll c[maxn]; 11 vector<int> eg[maxn],dep[maxn]; 12 vector<ll> op[maxn]; 13 ll ans[maxn]; 14 int n, m; 15 void add(int x, ll u) { 16 while(x <= n) { 17 c[x] += u; 18 x += lowbit(x); 19 } 20 } 21 ll sum(int x) { 22 ll res = 0; 23 while(x > 0) { 24 res += c[x]; 25 x -= lowbit(x); 26 } 27 return res; 28 } 29 void dfs(int now, int pre, int d) { 30 for(int i = 0; i < op[now].size(); ++i) { 31 add(min(d + dep[now][i], n), op[now][i]); 32 } 33 ans[now] = sum(n) - sum(d-1); 34 for(auto i: eg[now]) { 35 if(i == pre) continue; 36 dfs(i, now, d+1); 37 } 38 for(int i = 0; i < op[now].size(); ++i) { 39 add(min(d + dep[now][i], n), -op[now][i]); 40 } 41 } 42 int main(){ 43 44 scanf("%d", &n); 45 int x, y; 46 for(int i = 1; i < n; ++i) { 47 scanf("%d %d", &x, &y); 48 eg[x].push_back(y); 49 eg[y].push_back(x); 50 } 51 scanf("%d", &m); 52 int v, d; 53 ll xi; 54 for(int i = 1; i <= m; ++i) { 55 scanf("%d %d %lld", &v, &d, &xi); 56 if(d > n) d = n; 57 dep[v].push_back(d); 58 op[v].push_back(xi); 59 } 60 dfs(1,0,1); 61 for(int i = 1; i <= n; ++i) 62 printf("%lld ", ans[i]); 63 return 0; 64 }