题意:给定一个树形图,节点10^5,有两种操作,一种是把某两点间路径(路径必定唯一)上所有点的权值增加一个固定值。
另一种也是相同操作,不同的是给边加权值。操作次数10^5。求操作过后,每个点和每条边的权值。
分析:此题时间卡得非常紧,最好用输入外挂,最好不要用RMQ来求解LCA。
此题是典型的在线LCA问题,先讲讲在线LCA要怎么做。
在线LCA有两种方法,第一种比较常见,即将其转化成RMQ问题。
先对树形图进行深度优先遍历,遍历过程记录路线中点的途经序列,每个非叶子节点会在序列中出现多次,从一个节点A的一个子节点回到A点再走另一个子节点的时候要再次加A加入序列。
记录序列的同时还要记录序列中每个点在树中对应的深度。以及在序列中第一次出现的位置(其实不一定非要第一个才行),主要用于根据点标号查找其在序列中对应的下标。
此时,LCA已经转化为RMQ,如果要求a,b的LCA,只需要找到a,b在遍历序列中分别对应的位置,并在深度序列中查找以这两点为端点的区间内的最小值即可。这个最小值在遍历序列中对应的点就是他们的LCA。
这种方法预处理O(NlogN),查询是O(1)。
模板如下:
//first call init_LCA(root). //then call LCA(a, b) to quest the LCA of a and b. //the graph can be both bidirected or unidirected. #define MAX_NODE_NUM 0 #define MAX_EDGE_NUM 0 #define M 30 struct Edge { int v, next, id; Edge() {} Edge(int v, int next, int id):v(v), next(next), id(id) {} } edge[MAX_EDGE_NUM]; int head[MAX_NODE_NUM]; int edge_cnt; void init_edge() { memset(head, -1, sizeof(head)); edge_cnt = 0; } void add_edge(int u, int v, int id) { edge[edge_cnt] = Edge(v, head[u], id); head[u] = edge_cnt++; } bool vis[MAX_NODE_NUM]; int father[MAX_NODE_NUM]; int power[M]; int st[MAX_NODE_NUM * 2][M]; int ln[MAX_NODE_NUM * 2]; int seq_cnt; int seq[2*MAX_NODE_NUM]; int depth[2*MAX_NODE_NUM]; int first_appearance[MAX_NODE_NUM]; //returns the index of the first minimum value in [x, y] void init_RMQ(int f[], int n) { int i, j; for (power[0] = 1, i = 1; i < 21; i++) { power[i] = 2 * power[i - 1]; } for (i = 0; i < n; i++) { st[i][0] = i; } ln[0] = -1; for (int i = 1; i <= n; i++) { ln[i] = ln[i >> 1] + 1; } for (j = 1; j < ln[n]; j++) { for (i = 0; i < n; i++) { if (i + power[j - 1] - 1 >= n) { break; } //for maximum, change ">" to "<" //for the last, change "<" or ">" to "<=" or ">=" if (f[st[i][j - 1]] > f[st[i + power[j - 1]][j - 1]]) { st[i][j] = st[i + power[j - 1]][j - 1]; } else { st[i][j] = st[i][j - 1]; } } } } int query(int x, int y) { if(x > y) { swap(x, y); } int k = ln[y - x + 1]; //for maximum, change ">" to "<" //for the last, change "<" or ">" to "<=" or ">=" if (depth[st[x][k]] > depth[st[y - power[k] + 1][k]]) return st[y - power[k] + 1][k]; return st[x][k]; } void dfs(int u ,int current_depth) { vis[u] = true; first_appearance[u] = seq_cnt; depth[seq_cnt] = current_depth; seq[seq_cnt++] = u; for(int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].v; if (vis[v]) { continue; } father[v] = u; if (!vis[v]) { dfs(v, current_depth + 1); depth[seq_cnt] = current_depth; seq[seq_cnt++] = u; } } } void init_LCA(int root) { memset(vis, 0, sizeof(vis)); father[root] = -1; seq_cnt = 0; dfs(root, 0); init_RMQ(depth, seq_cnt); } //O(1) int LCA(int u ,int v) { int x = first_appearance[u]; int y = first_appearance[v]; int res = query(x, y); return seq[res]; }