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 }
由于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 */