题目:
题意:给定有n个点的一棵树,顶点1为根。m次操作,每次都把以v为根,深度dep以内的子树中所有的顶点(包括v本身)加x。求出最后每个点的值为多少。
解题思路:考虑到每次都只对点及其子树操作,要用dfs。设v当前要操作的点,操作的深度是dep,d[v]表示v的深度。要把深度[d[v],d[v]+dep]中所有点都加上x,暴力加是肯定不行的,于是想到要用树状数组或线段树。dfs+树状数组便是本题的基本思路。我们在搜索树的同时,维护以深度为下标的树状数组。为什么一个树形结构能够维护树状数组这样的线性结构呢?因为是对树的dfs,只要没有跳出点就一定搜一条线到底,这样搜索出来的点就能满足树状数组的线性。而每当跳出一个点,就把这个点给树状数组加的值减掉(回溯)即可。
附本人代码:
1 #include2 #include 3 #include 4 #include 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 eg[maxn],dep[maxn];12 vector 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 }