题目链接:http://www.spoj.com/problems/QTREE/en/
题意: 一棵树 n 个节点,每条边上有权值,同时有两个操作:
(1)更改操作:CHANGE i ti(把第 i 条边上的权值改为 ti)。
(2)查询操作:QUERY a b(查询 a 到 b 的路径上权值最大的边的权值)。
思路(树链剖分):
看到这种区间查询的题想要用数据结构优化,提高时间效率一般会想到线段树。可是这次操作的对象并不是一组序列,
无法直接使用线段树。这时,我们可以做些转化:对树作树链剖分,就是把树剖开成为一条条链,这时一条链可以作为线段树的一段连续区间。
通过剖分,这些链包括了树的 n - 1 条边,把这些边一一映射到线段树中(重新编号),这样就可已实现对树上两个节点的区间查询了。
那把树剖分成链有什么作用呢?
首先看一下是如何剖分的:
一些需要知道的名词:
重儿子:对于父节点 u ,所有以 u 的子节点为根的子树中包含最多节点的那个子树的根 v 就是 u 的重儿子。
轻儿子:父节点 u 所有的子节点中,不是重儿子的都是轻儿子。
重边:重儿子 v 与其父节点 u 之间的边。
轻边:轻儿子 v 与其父节点 u 之间的边。
重链:由重边组成的链就是重链了。正是有了重链,对其的编号是从上到下连续的,所以我们在查询时才可以节省了很多时间。
代码过程中,需要用到的数组(存储一些必要信息,经过 2 次 dfs 得到):
第一次 dfs : fa(father,节点的父节点),size(已该节点为根的子树节点数量),deep(当前节点在树中的深度),son(当前节点的重儿子)。
第二次 dfs : pos(position,树上的边在线段树上的位置),top(当前节点所在重链距离树根最近的节点)。
重新编号?:
第二次 dfs 过程中,对重儿子优先编号。因为是 dfs 保证了一条重链上的节点从上到下编号是连续递增的(对应线段树上的一段区间)。
这样我们的映射就完成了,可以对树作一定的查询了。
对于一个查询 QUERY a b :
(1)若 a,b 在同一条重链上:就可以直接在线段树上查询了(在同一个连续区间上)。
(2)若不在同一条重链上:则只能查询 a,b 所在重链上节点对应的边,有一条不是重边。这样逐步查询,一直逼近 a,b 的 LCA(最近公共祖先)。
代码:
1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 #include <math.h> 5 using namespace std; 6 7 const int N = 100007; 8 9 struct Edge 10 { 11 int to; 12 int next; 13 int w; 14 }edge[N]; 15 16 struct Node 17 { 18 int L; 19 int R; 20 int mid() 21 { 22 return (L + R) >> 1; 23 } 24 int maxn; 25 }bT[N]; 26 27 int cnt, e[N][3]; 28 int fa[N], head[N], size[N], son[N], top[N], deep[N], pos[N], fpos[N]; 29 30 void addEdge(int u, int v, int w, int idx) 31 { 32 edge[idx].to = v; 33 edge[idx].w = w; 34 edge[idx].next = head[u]; 35 head[u] = idx; 36 } 37 38 void dfs1(int u, int pre, int d) //deep, fa, son, size 39 { 40 deep[u] = d; 41 fa[u] = pre; 42 size[u] = 1; 43 for (int r = head[u]; r != -1; r = edge[r].next) 44 { 45 int v = edge[r].to; 46 if (v != pre) 47 { 48 dfs1(v, u, d + 1); 49 size[u] += size[v]; 50 if (son[u] == -1 || size[v] > size[son[u]]) 51 son[u] = v; 52 } 53 } 54 } 55 56 void getpos(int u, int anc) //pos, fpos, top 57 { 58 top[u] = anc; 59 pos[u] = cnt++; 60 fpos[pos[u]] = u; 61 if (son[u] == -1) 62 return; 63 getpos(son[u], anc); 64 for (int r = head[u]; r != -1; r = edge[r].next) 65 { 66 int v = edge[r].to; 67 if (v != fa[u] && son[u] != v) 68 getpos(v, v); 69 } 70 } 71 72 void push_up(int i) //线段树------begin------- 73 { 74 bT[i].maxn = max(bT[i<<1].maxn, bT[i<<1|1].maxn); 75 } 76 77 void build(int L, int R, int i) 78 { 79 bT[i].L = L; 80 bT[i].R = R; 81 bT[i].maxn = 0; 82 if (L == R) 83 return; 84 int mid = bT[i].mid(); 85 build(L, mid, i<<1); 86 build(mid + 1, R, i<<1|1); 87 } 88 89 void update(int l, int val, int i) 90 { 91 if (bT[i].L == l && bT[i].R == l) 92 { 93 bT[i].maxn = val; 94 return; 95 } 96 int mid = bT[i].mid(); 97 if (l <= mid) 98 update(l, val, i << 1); 99 else 100 update(l, val, i<<1|1); 101 push_up(i); 102 } 103 104 int query(int l, int r, int i) //--------end------ 105 { 106 if (bT[i].L == l && bT[i].R == r) 107 return bT[i].maxn; 108 int mid = bT[i].mid(); 109 if (r <= mid) 110 return query(l, r, i << 1); 111 if (l > mid) 112 return query(l, r, i<<1|1); 113 return max(query(l, mid, i<<1), query(mid + 1, r, i<<1|1)); 114 } 115 116 int find(int u, int v) //u,v的查询 117 { 118 int fu = top[u], fv = top[v], maxx = 0; 119 while (fu != fv) 120 { 121 if (deep[fu] < deep[fv]) 122 { 123 swap(u, v); 124 swap(fu, fv); 125 } 126 maxx = max(maxx, query(pos[fu], pos[u], 1)); 127 u = fa[fu]; 128 fu = top[u]; 129 } 130 if (u == v) 131 return maxx; 132 if (deep[u] > deep[v]) 133 swap(u, v); 134 return max(maxx, query(pos[son[u]], pos[v], 1)); 135 } 136 137 void init() 138 { 139 memset(size, 0, sizeof(size)); 140 memset(head, -1, sizeof(head)); 141 memset(son, -1, sizeof(son)); 142 } 143 144 int main() 145 { 146 #ifndef ONLINE_JUDGE 147 freopen("in.ads","r",stdin); 148 #endif 149 char op[100]; 150 int T, n; 151 while (scanf("%d", &T) != EOF) 152 { 153 while (T--) 154 { 155 scanf("%d", &n); 156 init(); 157 for (int i = 0; i < n - 1; i++) 158 { 159 scanf("%d%d%d", &e[i][0], &e[i][1], &e[i][2]); 160 addEdge(e[i][0], e[i][1], e[i][2], 2 * i); 161 addEdge(e[i][1], e[i][0], e[i][2], 2 * i + 1); 162 } 163 dfs1(1, 0, 0); 164 cnt = 0; 165 getpos(1, 1); 166 build(0, cnt - 1, 1); 167 for (int i = 0; i < n - 1; i++) 168 { 169 if (deep[e[i][0]] > deep[e[i][1]]) 170 swap(e[i][0], e[i][1]); 171 update(pos[e[i][1]], e[i][2], 1); 172 } 173 while (scanf("%s", op)) 174 { 175 int u, v; 176 if (op[0] == ‘D‘) 177 break; 178 else if (op[0] == ‘C‘) 179 { 180 scanf("%d%d", &u, &v); 181 update(pos[e[u-1][1]], v, 1); 182 } 183 else 184 { 185 scanf("%d%d", &u, &v); 186 printf("%d\n", find(u, v)); 187 } 188 getchar(); 189 } 190 } 191 } 192 return 0; 193 }