用途:在一棵树上进行修改、求极值、求和。
时间复杂度:,数据范围100,000;
概念:
-
重儿子:节点u所有的儿子中,siz最大的儿子;
-
轻儿子:节点u所有的非重儿子的儿子;
-
重链:所有连续重儿子连接成的链;
-
轻链(轻边):由节点u跟轻儿子连接的边,最多包含两个定点。
结论:
-
所有的重链个数不超过.
-
所有的轻边的个数不超过;
-
轻边的size*2<SIZE;
维护的数据
-
val[u] 节点u的价值
-
tid[u] 节点u的新编号,用于维护线段树
-
valt[u] 节点u的新编号对应的价值,用于维护线段树
-
size[u] 以u为根的子树的节点数; 用于区分重儿子和轻儿子。
-
dep[u] 节点v的深度,根节点的深度为1或0;
-
top[u] 节点u所在链的顶端节点,(u,v)如果为重链,top[u]==top[v],轻链或者轻链重链的结合,top[u]!=top[v].
-
fa[u] 表示u节点的额父亲节点
实现过程:
-
邻接表存树
-
DFS1,维护出fa[],dep[]和siz[]
-
DFS2,优先访问重儿子,维护出tid[],valt[],top[]
-
用tid[]和valt[]建立线段树。
-
修改,修改u的tid[u]在线段树上的值即可。
-
查询,两部查询,把查询的链分成重链和轻链查询。如果是复合链,优先查询dep[top[u]]深度深的一端。防止查询越界。
例题:BZOJ1036树的统计Count
-
一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II。QMAX u v: 询问从点u到点v的路径上的节点的最大权值。 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和。注意:从点u到点v的路径上的节点包括u和v本身。
-
输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。 对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。
-
对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。
#include<bits/stdc++.h> using namespace std; const int maxn = 3e4 + 15; int N, Q, u, v; //-----------------------邻接表 struct node { int id, next; } E[maxn << 1]; //双向边 int head[maxn], num; void init() { memset(head, -1, sizeof(head)); num = 0; } void adde(int u, int v) { E[num].id = v; E[num].next = head[u]; head[u] = num++; } //--------------------------------DFS1/2 int val[maxn], valt[maxn], tid[maxn], siz[maxn], top[maxn], dep[maxn], fa[maxn]; int clk; void DFS1(int u, int p) { dep[u] = dep[p] + 1; fa[u] = p; siz[u] = 1; for(int k = head[u]; k != -1; k = E[k].next) { int id = E[k].id; if(id == p) continue; DFS1(id, u); siz[u] += siz[id]; } } void DFS2(int u, int p) { tid[u] = ++clk; valt[clk] = val[u]; int son = -1; for(int k = head[u]; k != -1; k = E[k].next) { int id = E[k].id; if(id == p)continue; if(son == -1 || siz[id] > siz[son]) son = id; } if(son != -1) { top[son] = top[u]; DFS2(son, u); } for(int k = head[u]; k != -1; k = E[k].next) { int id = E[k].id; if(id == p || id == son)continue; top[id] = id; DFS2(id, u); } } //--------------------------------线段树 int mx[maxn*4], sum[maxn*4]; void build(int id, int l, int r) { if(l == r) mx[id] = sum[id] = valt[l]; else { int mid = (l + r) >> 1; build(id << 1, l, mid); build(id << 1 | 1, mid + 1, r); mx[id] = max(mx[id << 1], mx[id << 1 | 1]); sum[id] = sum[id << 1] + sum[id << 1 | 1]; } } void update(int id, int l, int r, int u, int vual) { if(l == r) mx[id] = sum[id] = vual; else { int mid = (l + r) >> 1; if(u <= mid) update(id << 1, l, mid, u, vual); else update(id << 1 | 1, mid + 1, r, u, vual); mx[id] = max(mx[id << 1], mx[id << 1 | 1]); sum[id] = sum[id << 1] + sum[id << 1 | 1]; } } int QMax(int id, int L, int R, int l, int r) { if(R <= r && L >= l) return mx[id]; else { int mid = (L + R) >> 1; int ret = -INT_MAX; if(l <= mid)ret = max(ret, QMax(id << 1, L, mid, l, r)); if(r >= mid + 1) ret = max(ret, QMax(id << 1 | 1, mid + 1, R, l, r)); return ret; } } int QMax(int u, int v) { int ret = -INT_MAX; while(top[u] != top[v]) { if(dep[top[u]] < dep[top[v]]) swap(u, v); ret = max(ret, QMax(1, 1, N, tid[top[u]], tid[u])); u = fa[top[u]]; } if(dep[u] < dep[v]) swap(u, v); ret = max(ret, QMax(1, 1, N, tid[v], tid[u])); return ret; } int Qsum(int id, int L, int R, int l, int r) { if(R <= r && L >= l) return sum[id]; else { int mid = (L + R) >> 1; int ret = 0; if(l <= mid)ret += Qsum(id << 1, L, mid, l, r); if(r >= mid + 1) ret += Qsum(id << 1 | 1, mid + 1, R, l, r); return ret; } } int Qsum(int u, int v) { int ret = 0; while(top[u] != top[v]) { if(dep[top[u]] < dep[top[v]]) swap(u, v); { ret += Qsum(1, 1, N, tid[top[u]], tid[u]); u = fa[top[u]]; } } if(dep[u] < dep[v]) swap(u, v); ret += Qsum(1, 1, N, tid[v], tid[u]); return ret; } //--------------------------------主函数 int main () { init(); scanf("%d",&N); for(int i = 1; i < N; i++) { scanf("%d %d", &u, &v); adde(u, v); adde(v, u); } for(int i = 1; i <= N; i++) scanf("%d", &val[i]); DFS1(1, 0); top[1] = 1; DFS2(1, 0); build(1, 1, N); char s[10]; scanf("%d", &Q); for(int i = 1; i <= Q; i++) { scanf("%s%d%d", s, &u, &v); if(!strcmp(s, "QMAX")) printf("%d\n", QMax(u, v)); else if(!strcmp(s, "QSUM")) printf("%d\n",Qsum(u, v)); else { update(1, 1, N, tid[u], v); } } return 0; }
来源:https://www.cnblogs.com/pealicx/p/7622499.html