DFS序常见用法及代码实现

dfs序就是一棵树在dfs遍历时组成的节点序列.

它有这样一个特点:一棵子树的dfs序是一个区间. 下面是dfs序的基本代码:

 1 void dfs(int x,int pre,int d){//L,R表示一个子树的范围
 2     L[x]=++tot;
 3     dep[x]=d;
 4     for(int i=0;i<e[x].size();i++){
 5         int y=e[x][i];
 6         if(y==pre)continue;
 7         dfs(y,x,d+1);
 8     }
 9     R[x]=tot;
10 }

给定一颗树, 和每个节点的权值.下面有7个经典的关于dfs序的问题:

下面所有问题都采用如图的树作为样例

都有以下初始化建树代码:

 1 const int n = 15;
 2 const int maxn = 1010;
 3 struct E{
 4     int to,next;
 5 }edge[maxn];
 6 int head[maxn], cnt;
 7 int t[maxn];
 8 inline void addedge(int u,int v){
 9     edge[cnt] = (E){v,head[u]};
10     head[u] = cnt++;
11     edge[cnt] = (E){u,head[v]};
12     head[v] = cnt++;
13 }
14 void build(){
15     memset(head, -1, sizeof head);
16     int tmp[28] = {1,2,1,3,2,4,2,5,2,6,3,7,3,8,5,9,5,10,7,11,7,12,12,13,12,14,12,15};
17     for (int i = 0; i < 14*2; i+=2){
18         addedge(tmp[i],tmp[i+1]);
19     }
20 }

1.对某个节点X权值加上一个数W, 查询某个子树X里所有点权的和.

由于X的子树在DFS序中是连续的一段, 只需要维护一个dfs序列,用树状数组实现:单点修改和区间查询.

代码实现:

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 
 6 const int n = 15;
 7 const int maxn = 1010;
 8 struct E{
 9     int to,next;
10 }edge[maxn];
11 int head[maxn], cnt;
12 int t[maxn];
13 inline void addedge(int u,int v){
14     edge[cnt] = (E){v,head[u]};
15     head[u] = cnt++;
16     edge[cnt] = (E){u,head[v]};
17     head[v] = cnt++;
18 }
19 void build(){
20     memset(head, -1, sizeof head);
21     int tmp[28] = {1,2,1,3,2,4,2,5,2,6,3,7,3,8,5,9,5,10,7,11,7,12,12,13,12,14,12,15};
22     for (int i = 0; i < 14*2; i+=2){
23         addedge(tmp[i],tmp[i+1]);
24     }
25 }
26 
27 
28 int L[maxn], R[maxn], tot; //l[pos]表示子树的根(编号为pos的子树的dfs序号) R[pos]表示子树结尾的点 L[pos] ~ R[pos]表示以pos为根的子树 
29 void dfs(int u,int pre){
30     L[u] = ++tot;
31     for (int i = head[u]; ~i; i = edge[i].next){
32         int v = edge[i].to;
33         if (v == pre) continue;
34         dfs(v,u);
35     }
36     R[u] = tot;
37 }
38 
39 
40 int tree[maxn];
41 int lowbit(int x){return x & -x;}
42 int add(int x,int w){
43     while (x <= n){
44         tree[x] += w;
45         x += lowbit(x);
46     }
47 }//对每个节点x加上w 
48 int sum(int x){
49     int res = 0;
50     while (x > 0){
51         res += tree[x];
52         x -= lowbit(x);
53     }
54     return res;
55 }
56 int query(int x){
57     return sum(R[x]) - sum(L[x]-1);
58 }// 查询编号为x的子树里所有点的权值和 
59 
60 
61 int main(){
62     build();
63     dfs(1,-1);
64     string op; // 输入Q x表示查询编号为x的子树里所有点权值和 Add x w表示在x节点加上w的权值 
65     int x, w;
66     while (cin >> op){
67         if (op == "Q"){
68             cin >> x;
69             cout << query(x) << endl;
70         } else {
71             cin >> x >> w;
72             add(L[x], w);
73         }
74     }
75     return 0;
76 }
77 /*
78 input : 
79 Add 5 1
80 Add 6 1
81 Q 2
82 Add 7 2
83 Add 12 3
84 Add 13 1
85 Q 12
86 Q 3
87 Q 1
88 output : 
89 2
90 4
91 6
92 8
93 */

 2. 对节点X到Y的最短路上所有点权都加一个数W, 查询某个点的权值.
这个操作等价于

a. 对X到根节点路径上所有点权加W
b. 对Y到根节点路径上所有点权加W
c. 对LCA(x, y)到根节点路径上所有点权值减W
d. 对LCA(x,y)的父节点 fa(LCA(x, y))到根节点路径上所有权值减W

于是要进行四次这样从一个点到根节点的区间修改.将问题进一步简化, 进行一个点X到根节点的区间修改, 查询其他一点Y时,只有X在Y的子树内, X对Y的值才有贡献且贡献值为W.当单点更新X时,X实现了对X到根的路径上所有点贡献了W.于是只需要更新四个点(单点更新) ,查询一个点的子树内所有点权的和(区间求和)即可.

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 const int n = 15;
  4 const int maxn = 1010;
  5 
  6 const int lca_maxn = 1010;
  7 const int tree_deep = 25;
  8 int fa[lca_maxn][tree_deep];
  9 int dep[lca_maxn], lg[lca_maxn];
 10 
 11 void init_lg(){
 12     for(int i = 1; i <= n; ++i) //log_2(i)+1
 13     lg[i] = lg[i-1] + (1 << lg[i-1] == i);
 14 }
 15 
 16 struct E{
 17     int to,next;
 18 }edge[maxn];
 19 int head[maxn], cnt;
 20 int t[maxn];
 21 inline void addedge(int u,int v){
 22     edge[cnt] = (E){v,head[u]};
 23     head[u] = cnt++;
 24     edge[cnt] = (E){u,head[v]};
 25     head[v] = cnt++;
 26 }
 27 void build(){
 28     memset(head, -1, sizeof head);
 29     int tmp[28] = {1,2,1,3,2,4,2,5,2,6,3,7,3,8,5,9,5,10,7,11,7,12,12,13,12,14,12,15};
 30     for (int i = 0; i < 14*2; i+=2){
 31         addedge(tmp[i],tmp[i+1]);
 32     }
 33 }
 34 
 35 
 36 void dfs_lca(int u,int pre){
 37     fa[u][0] = pre, dep[u] = dep[pre] + 1;
 38     for (register int i = 1; i <= lg[dep[u]]; ++i){
 39         fa[u][i] = fa[fa[u][i-1]][i-1];
 40     }
 41     for (register int i = head[u]; ~i; i = edge[i].next){
 42         int v = edge[i].to; if (v != pre) dfs_lca(v,u);
 43     }
 44 }
 45 int lca(int x, int y){
 46     if (dep[x] < dep[y]) swap(x, y);
 47     while (dep[x] > dep[y]) x = fa[x][lg[dep[x] - dep[y]] - 1];
 48     if (x == y) return x;
 49     for (register int i = lg[dep[x] - 1]; i >= 0; --i){
 50         if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
 51     }
 52     return fa[x][0];
 53 }
 54 
 55 int L[maxn], R[maxn], tot, father[maxn];
 56 int dfs(int u, int pre){
 57     father[u] = pre;
 58     L[u] = ++tot;
 59     for (int i = head[u]; ~i; i = edge[i].next) {
 60         int v = edge[i].to;
 61         if (v == pre) continue;
 62         dfs(v, u);
 63     }
 64     R[u] = tot;
 65 }
 66 
 67 int tree[maxn];
 68 int lowbit(int x) { return x & -x;}
 69 void update(int x, int val){
 70     while (x <= n) {
 71         tree[x] += val;
 72         x += lowbit(x);
 73     }
 74 }
 75 int sum (int x){
 76     int res = 0;
 77     while (x > 0) {
 78         res += tree[x];
 79         x -= lowbit(x);
 80     }
 81     return res;
 82 }
 83 int query (int x){
 84     return sum(R[x]) - sum(L[x]-1);
 85 }
 86 
 87 int add(int x,int y, int v){
 88     int LCA = lca(x, y);
 89     update(L[x], v);
 90     update(L[y], v);
 91     update(L[LCA], -v);
 92     if (father[LCA])
 93     update(L[father[LCA]], -v);
 94 }
 95 
 96 int main(){
 97     init_lg();
 98     build();
 99     dfs(1, 0);
100     dfs_lca(1,0);
101     string op;
102     int x, y, w;
103     while (cin >> op) {
104         if (op == "Q") {// 输入Q x表示查询编号为x的点的权值 Add x y w表示在x节点到y节点的最短路上加上w的权值
105             cin >> x;
106             cout << query(x) << endl;
107         } else {
108             cin >> x >> y >> w;
109             add(x, y, w);
110         } 
111     }
112     return 0;
113 }
114 /*
115 input : 
116 Add 10 12 1
117 Add 3 9 2
118 Q 1
119 Q 11
120 Q 5
121 Q 9
122 Add 1 1 -1
123 Q 1
124 output : 
125 3
126 0
127 3
128 2
129 2
130 */

 3.对节点X到Y的最短路上所有点权都加一个数W, 查询某个点子树的权值之和
同问题2中的修改方法, 转化为修改某点到根节点的权值加/减W
当修改某个节点A, 查询另一节点B时
只有A在B的子树内, Y的值会增加W×(depth[A]depth[B]+1)==W×(depth[A]+1)W×depth[B]W×(depth[A]−depth[B]+1)==W×(depth[A]+1)−W×depth[B]
同样是用树状数组来查询Y的子树, 修改 和 查询方法都要新增一个数组
第一个数组保存 W×(depth[A]+1)W×(depth[A]+1), 第二个数组保存 W
每次查询结果为Sum(ed[B])Sum(st[B]1)(Sum1(ed[B])Sum(st[B]1))×depth[B]

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 const int n = 15;
  4 const int maxn = 1010;
  5 
  6 const int lca_maxn = 1010;
  7 const int tree_deep = 25;
  8 int fa[lca_maxn][tree_deep];
  9 int dep[lca_maxn], lg[lca_maxn];
 10 
 11 void init_lg(){
 12     for(int i = 1; i <= n; ++i) //log_2(i)+1
 13     lg[i] = lg[i-1] + (1 << lg[i-1] == i);
 14 }
 15 
 16 struct E{
 17     int to,next;
 18 }edge[maxn];
 19 int head[maxn], cnt;
 20 int t[maxn];
 21 inline void addedge(int u,int v){
 22     edge[cnt] = (E){v,head[u]};
 23     head[u] = cnt++;
 24     edge[cnt] = (E){u,head[v]};
 25     head[v] = cnt++;
 26 }
 27 void build(){
 28     memset(head, -1, sizeof head);
 29     int tmp[28] = {1,2,1,3,2,4,2,5,2,6,3,7,3,8,5,9,5,10,7,11,7,12,12,13,12,14,12,15};
 30     for (int i = 0; i < 14*2; i+=2){
 31         addedge(tmp[i],tmp[i+1]);
 32     }
 33 }
 34 
 35 
 36 void dfs_lca(int u,int pre){
 37     fa[u][0] = pre, dep[u] = dep[pre] + 1;
 38     for (register int i = 1; i <= lg[dep[u]]; ++i){
 39         fa[u][i] = fa[fa[u][i-1]][i-1];
 40     }
 41     for (register int i = head[u]; ~i; i = edge[i].next){
 42         int v = edge[i].to; if (v != pre) dfs_lca(v,u);
 43     }
 44 }
 45 int lca(int x, int y){
 46     if (dep[x] < dep[y]) swap(x, y);
 47     while (dep[x] > dep[y]) x = fa[x][lg[dep[x] - dep[y]] - 1];
 48     if (x == y) return x;
 49     for (register int i = lg[dep[x] - 1]; i >= 0; --i){
 50         if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
 51     }
 52     return fa[x][0];
 53 }
 54 
 55 int L[maxn], R[maxn], tot, father[maxn];
 56 int dfs(int u, int pre){
 57     father[u] = pre;
 58     L[u] = ++tot;
 59     for (int i = head[u]; ~i; i = edge[i].next) {
 60         int v = edge[i].to;
 61         if (v == pre) continue;
 62         dfs(v, u);
 63     }
 64     R[u] = tot;
 65 }
 66 
 67 int tree1[maxn];
 68 int lowbit(int x) { return x & -x;}
 69 void update1(int x, int val){
 70     while (x <= n) {
 71         tree1[x] += val;
 72         x += lowbit(x);
 73     }
 74 }
 75 int sum1 (int x){
 76     int res = 0;
 77     while (x > 0) {
 78         res += tree1[x];
 79         x -= lowbit(x);
 80     }
 81     return res;
 82 }
 83 
 84 int tree2[maxn];
 85 void update2(int x,int val) {
 86     while (x <= n) {
 87         tree2[x] += val;
 88         x += lowbit(x);
 89     }
 90 }
 91 int sum2 (int x){
 92     int res = 0;
 93     while (x > 0){
 94         res += tree2[x];
 95         x -= lowbit(x);
 96     }
 97     return res;
 98 }
 99 int add(int x,int y, int v){
100     int LCA = lca(x, y);
101     update1(L[x], v);
102     update2(L[x], (v*(dep[x]+1)));
103     update1(L[y], v);
104     update2(L[y], (v*(dep[y]+1)));
105     update1(L[LCA], -v);
106     update2(L[LCA], (-v*(dep[LCA]+1)));
107     if (father[LCA])
108     update1(L[father[LCA]], -v),update2(L[father[LCA]],(-v*(dep[father[LCA]]+1)));
109 }
110 
111 int query(int x){
112     return sum2(R[x]) - sum2(L[x]-1) - dep[x] * (sum1(R[x]) - sum1(L[x]-1));
113 }
114 
115 int main(){
116     init_lg();
117     build();
118     dfs(1, 0);
119     dfs_lca(1,0);
120     string op;
121     int x, y, w;
122     while (cin >> op) {
123         if (op == "Q") {// 输入Q x表示查询编号为x的子树里所有点权值和 Add x y w表示在x节点到y节点的最短路上加上w的权值
124             cin >> x;
125             cout << query(x) << endl;
126         } else {
127             cin >> x >> y >> w;
128             add(x, y, w);
129         } 
130     }
131     return 0;
132 }
133 /*
134 input : 
135 Add 9 11 1
136 Q 2
137 Q 1
138 Q 7
139 Add 3 15 2
140 Q 3
141 output : 
142 3
143 7
144 2
145 11
146 */

4.单点更新,树链和查询

树链和查询与树链修改类似,树链和(x,y)等于下面四个部分和相加:

1).x到根节点的链上所有节点权值加。

2).y到根节点的链上所有节点权值加。

3).lca(x,y)到根节点的链上所有节点权值和的-1倍。

4).fa(lca(x,y))到根节点的链上所有节点权值和的-1倍。

所以问题转化为:查询点x到根节点的链上的所有节点权值和。

修改节点x权值,当且仅当y是x的子孙节点时,x对y的值有贡献。

差分前缀和,y的权值等于dfs中[1,l[y]]的区间和。

单点修改:add(l[x],v),add(r[x]+1,-v);

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 const int n = 15;
  4 const int maxn = 1010;
  5 
  6 const int lca_maxn = 1010;
  7 const int tree_deep = 25;
  8 int fa[lca_maxn][tree_deep];
  9 int dep[lca_maxn], lg[lca_maxn];
 10 
 11 void init_lg(){
 12     for(int i = 1; i <= n; ++i) //log_2(i)+1
 13     lg[i] = lg[i-1] + (1 << lg[i-1] == i);
 14 }
 15 
 16 struct E{
 17     int to,next;
 18 }edge[maxn];
 19 int head[maxn], cnt;
 20 int t[maxn];
 21 inline void addedge(int u,int v){
 22     edge[cnt] = (E){v,head[u]};
 23     head[u] = cnt++;
 24     edge[cnt] = (E){u,head[v]};
 25     head[v] = cnt++;
 26 }
 27 void build(){
 28     memset(head, -1, sizeof head);
 29     int tmp[28] = {1,2,1,3,2,4,2,5,2,6,3,7,3,8,5,9,5,10,7,11,7,12,12,13,12,14,12,15};
 30     for (int i = 0; i < 14*2; i+=2){
 31         addedge(tmp[i],tmp[i+1]);
 32     }
 33 }
 34 
 35 
 36 void dfs_lca(int u,int pre){
 37     fa[u][0] = pre, dep[u] = dep[pre] + 1;
 38     for (register int i = 1; i <= lg[dep[u]]; ++i){
 39         fa[u][i] = fa[fa[u][i-1]][i-1];
 40     }
 41     for (register int i = head[u]; ~i; i = edge[i].next){
 42         int v = edge[i].to; if (v != pre) dfs_lca(v,u);
 43     }
 44 }
 45 int lca(int x, int y){
 46     if (dep[x] < dep[y]) swap(x, y);
 47     while (dep[x] > dep[y]) x = fa[x][lg[dep[x] - dep[y]] - 1];
 48     if (x == y) return x;
 49     for (register int i = lg[dep[x] - 1]; i >= 0; --i){
 50         if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
 51     }
 52     return fa[x][0];
 53 }
 54 
 55 int L[maxn], R[maxn], tot, father[maxn];
 56 int dfs(int u, int pre){
 57     father[u] = pre;
 58     L[u] = ++tot;
 59     for (int i = head[u]; ~i; i = edge[i].next) {
 60         int v = edge[i].to;
 61         if (v == pre) continue;
 62         dfs(v, u);
 63     }
 64     R[u] = tot;
 65 }
 66 
 67 int tree[maxn];
 68 int lowbit(int x) { return x & -x;}
 69 void update(int x, int val){
 70     while (x <= n) {
 71         tree[x] += val;
 72         x += lowbit(x);
 73     }
 74 }
 75 int sum (int x){
 76     int res = 0;
 77     while (x > 0) {
 78         res += tree[x];
 79         x -= lowbit(x);
 80     }
 81     return res;
 82 }
 83 int query (int x,int y){
 84     int LCA = lca(x, y);
 85     return sum(L[x]) + sum(L[y]) - sum(L[LCA]) - sum(L[father[LCA]]);
 86 }
 87 
 88 int add(int x, int v){
 89     update(L[x], v);
 90     update(R[x] + 1, -v);
 91 }
 92 
 93 int main(){
 94     init_lg();
 95     build();
 96     dfs(1, 0);
 97     dfs_lca(1,0);
 98     string op;
 99     int x, y, w;
100     while (cin >> op) {
101         if (op == "Q") {// 输入Q x y表示查询编号为x和y的点的所在链权值和 Add x w表示在x节点上加上w的权值
102             cin >> x >> y;
103             cout << query(x, y) << endl;
104         } else {
105             cin >> x >> w;
106             add(x, w);
107         } 
108     }
109     return 0;
110 }
111 /*
112 input : 
113 Add 5 2
114 Q 9 2
115 Q 2 5
116 Add 2 1
117 Add 1 3
118 Add 12 10
119 Q 15 3
120 Q 10 3
121 output :
122 2
123 2
124 10
125 6 
126 */

5.子树修改,单点查询

修改节点x的子树权值,在dfs序上就是区间修改,单点权值查询就是单点查询。

区间修改,单点查询问题:树状数组或线段树即可;

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 
 6 const int n = 15;
 7 const int maxn = 1010;
 8 struct E{
 9     int to,next;
10 }edge[maxn];
11 int head[maxn], cnt;
12 int t[maxn];
13 inline void addedge(int u,int v){
14     edge[cnt] = (E){v,head[u]};
15     head[u] = cnt++;
16     edge[cnt] = (E){u,head[v]};
17     head[v] = cnt++;
18 }
19 void build(){
20     memset(head, -1, sizeof head);
21     int tmp[28] = {1,2,1,3,2,4,2,5,2,6,3,7,3,8,5,9,5,10,7,11,7,12,12,13,12,14,12,15};
22     for (int i = 0; i < 14*2; i+=2){
23         addedge(tmp[i],tmp[i+1]);
24     }
25 }
26 
27 
28 int L[maxn], R[maxn], tot; //l[pos]表示子树的根(编号为pos的子树的dfs序号) R[pos]表示子树结尾的点 L[pos] ~ R[pos]表示以pos为根的子树 
29 void dfs(int u,int pre){
30     L[u] = ++tot;
31     for (int i = head[u]; ~i; i = edge[i].next){
32         int v = edge[i].to;
33         if (v == pre) continue;
34         dfs(v,u);
35     }
36     R[u] = tot;
37 }
38 
39 
40 int tree[maxn];
41 int lowbit(int x){return x & -x;}
42 int add(int x,int w){
43     while (x <= n){
44         tree[x] += w;
45         x += lowbit(x);
46     }
47 }//对每个节点x加上w 
48 int sum(int x){
49     int res = 0;
50     while (x > 0){
51         res += tree[x];
52         x -= lowbit(x);
53     }
54     return res;
55 }
56 
57 void update (int x,int val) {
58     add(L[x], val);
59     add(R[x]+1, -val);
60 }
61 
62 int main(){
63     build();
64     dfs(1,-1);
65     string op; // 输入Q x表示查询编号为x的权值 Add x w表示在编号为x的子树里加上w的权值 
66     int x, w;
67     while (cin >> op){
68         if (op == "Q"){
69             cin >> x;
70             cout << sum(L[x]) << endl;
71         } else {
72             cin >> x >> w;
73             update(x, w);
74         }
75     }
76     return 0;
77 }
78 /*
79 input : 
80 Add 2 1
81 Add 3 2
82 Q 5
83 Q 10
84 Q 8
85 Q 12
86 Q 15
87 Add 1 100
88 Q 4
89 Q 11 
90 output : 
91 1
92 1
93 2
94 2
95 2
96 101
97 102
98 */

6.子树修改,子树和查询

题目等价与区间修改,区间查询问题。用树状数组或线段树即可。

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 
 6 const int n = 15;
 7 const int maxn = 1010;
 8 struct E{
 9     int to,next;
10 }edge[maxn];
11 int head[maxn], cnt;
12 int t[maxn];
13 inline void addedge(int u,int v){
14     edge[cnt] = (E){v,head[u]};
15     head[u] = cnt++;
16     edge[cnt] = (E){u,head[v]};
17     head[v] = cnt++;
18 }
19 void build(){
20     memset(head, -1, sizeof head);
21     int tmp[28] = {1,2,1,3,2,4,2,5,2,6,3,7,3,8,5,9,5,10,7,11,7,12,12,13,12,14,12,15};
22     for (int i = 0; i < 14*2; i+=2){
23         addedge(tmp[i],tmp[i+1]);
24     }
25 }
26 
27 
28 int L[maxn], R[maxn], tot; //l[pos]表示子树的根(编号为pos的子树的dfs序号) R[pos]表示子树结尾的点 L[pos] ~ R[pos]表示以pos为根的子树 
29 void dfs(int u,int pre){
30     L[u] = ++tot;
31     for (int i = head[u]; ~i; i = edge[i].next){
32         int v = edge[i].to;
33         if (v == pre) continue;
34         dfs(v,u);
35     }
36     R[u] = tot;
37 }
38 
39 int sum1[maxn], sum2[maxn];
40 void add(int p, int x){
41     for(int i = p; i <= n; i += i & -i)
42         sum1[i] += x, sum2[i] += x * p;
43 }
44 void range_add(int l, int r, int x){
45     add(l, x), add(r + 1, -x);
46 }
47 int ask(int p){
48     int res = 0;
49     for(int i = p; i; i -= i & -i)
50         res += (p + 1) * sum1[i] - sum2[i];
51     return res;
52 }
53 int range_ask(int l, int r){
54     return ask(r) - ask(l - 1);
55 }
56 
57 int main(){
58     build();
59     dfs(1,-1);
60     string op; // 输入Q x表示查询编号为x的权值 Add x w表示在编号为x的子树里加上w的权值 
61     int x, w;
62     while (cin >> op){
63         if (op == "Q"){
64             cin >> x;
65             cout << range_ask(L[x], R[x]) << endl;
66         } else {
67             cin >> x >> w;
68             range_add(L[x], R[x], w);
69         }
70     }
71     return 0;
72 }
73 /*
74 input : 
75 Add 2 1
76 Q 1
77 Add 3 1
78 Q 1
79 Q 5
80 Add 10 100
81 Q 5
82 output : 
83 6
84 14
85 3
86 103
87 */

 7. 对节点X的子树所有节点加上一个值W, 查询X到Y的路径上所有点的权值和
同问题4把路径上求和转化为四个点到根节点的和
X到根节点的和 + Y到根节点的和 - LCA(x, y)到根节点的和 - parent(LCA(x,y)) 到根节点的
再用刷漆只更新子树.
修改一点A, 查询某点B到根节点时, 只有B在A的子树内, A对B才有贡献.
贡献为W * (dep[B] - dep[A] + 1) => W * (1 - dep[A]) + W * dep[B]
和第三题一样, 用两个sum1,sum2维护 W *(dep[A] + 1),和W.
最后答案就是sum2*dep[B]-sum1.

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 const int n = 15;
  4 const int maxn = 1010;
  5 
  6 const int lca_maxn = 1010;
  7 const int tree_deep = 25;
  8 int fa[lca_maxn][tree_deep];
  9 int dep[lca_maxn], lg[lca_maxn];
 10 
 11 void init_lg(){
 12     for(int i = 1; i <= n; ++i) //log_2(i)+1
 13     lg[i] = lg[i-1] + (1 << lg[i-1] == i);
 14 }
 15 
 16 struct E{
 17     int to,next;
 18 }edge[maxn];
 19 int head[maxn], cnt;
 20 int t[maxn];
 21 inline void addedge(int u,int v){
 22     edge[cnt] = (E){v,head[u]};
 23     head[u] = cnt++;
 24     edge[cnt] = (E){u,head[v]};
 25     head[v] = cnt++;
 26 }
 27 void build(){
 28     memset(head, -1, sizeof head);
 29     int tmp[28] = {1,2,1,3,2,4,2,5,2,6,3,7,3,8,5,9,5,10,7,11,7,12,12,13,12,14,12,15};
 30     for (int i = 0; i < 14*2; i+=2){
 31         addedge(tmp[i],tmp[i+1]);
 32     }
 33 }
 34 
 35 
 36 void dfs_lca(int u,int pre){
 37     fa[u][0] = pre, dep[u] = dep[pre] + 1;
 38     for (register int i = 1; i <= lg[dep[u]]; ++i){
 39         fa[u][i] = fa[fa[u][i-1]][i-1];
 40     }
 41     for (register int i = head[u]; ~i; i = edge[i].next){
 42         int v = edge[i].to; if (v != pre) dfs_lca(v,u);
 43     }
 44 }
 45 int lca(int x, int y){
 46     if (dep[x] < dep[y]) swap(x, y);
 47     while (dep[x] > dep[y]) x = fa[x][lg[dep[x] - dep[y]] - 1];
 48     if (x == y) return x;
 49     for (register int i = lg[dep[x] - 1]; i >= 0; --i){
 50         if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
 51     }
 52     return fa[x][0];
 53 }
 54 
 55 int L[maxn], R[maxn], tot, father[maxn];
 56 int dfs(int u, int pre){
 57     father[u] = pre;
 58     L[u] = ++tot;
 59     for (int i = head[u]; ~i; i = edge[i].next) {
 60         int v = edge[i].to;
 61         if (v == pre) continue;
 62         dfs(v, u);
 63     }
 64     R[u] = tot;
 65 }
 66 
 67 int tree1[maxn];
 68 int lowbit(int x) { return x & -x;}
 69 void update1(int x, int val){
 70     while (x <= n) {
 71         tree1[x] += val;
 72         x += lowbit(x);
 73     }
 74 }
 75 int sum1 (int x){
 76     int res = 0;
 77     while (x > 0) {
 78         res += tree1[x];
 79         x -= lowbit(x);
 80     }
 81     return res;
 82 }
 83 
 84 int tree2[maxn];
 85 void update2(int x,int val) {
 86     while (x <= n) {
 87         tree2[x] += val;
 88         x += lowbit(x);
 89     }
 90 }
 91 int sum2 (int x){
 92     int res = 0;
 93     while (x > 0){
 94         res += tree2[x];
 95         x -= lowbit(x);
 96     }
 97     return res;
 98 }
 99 int add(int x, int v){
100     update1(L[x], v);
101     update2(L[x], (v*(1-dep[x])));
102     update1(R[x]+1, -v);
103     update2(R[x]+1, (-v*(1-dep[x])));
104 }
105 
106 int Q(int x) {
107     return sum2(L[x]) + sum1(L[x]) * dep[x];
108 }
109 
110 int query(int x, int y){
111     int LCA = lca(x, y);
112     return Q(x) + Q(y) - Q(LCA) - Q(father[LCA]);
113 }
114 
115 int main(){
116     init_lg();
117     build();
118     dfs(1, 0);
119     dfs_lca(1,0);
120     string op;
121     int x, y, w;
122     while (cin >> op) {
123         if (op == "Q") {// 输入Q x y表示查询树链x到y的权值和 add x w表示在x的子树内所有点权值加上w 
124             cin >> x >> y;
125             cout << query(x, y) << endl;
126         } else {
127             cin >> x >> w;
128             add(x, w);
129         } 
130     }
131     return 0;
132 }
133 /*
134 input : 
135 Add 2 1
136 Q 6 9
137 Add 1 1
138 Q 1 15
139 Q 3 9
140 Q 2 2
141 Add 3 2
142 Q 3 11
143 output : 
144 4
145 5
146 8
147 2
148 9
149 */
原文地址:https://www.cnblogs.com/hznudreamer/p/12688749.html