[SinGuLaRiTy] tree chain and singularity
[SinGuLaRiTy-1035] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved.
About tree chains
What is a tree chain? At first glance, it seems that a strange word expresses the path from one node to another in a tree. In many seemingly complex graph theory problems, we only need to properly process the graph and convert it into a tree, and the problem will be simplified, then we can easily solve the problem by using the related ideas of the tree chain.
Multiply on the tree
The so-called "tree doubling" is actually a method for recording nodes. We define an array f [I] [j], which indicates the 2nd ^ j ancestor of node I (usually the father of node I is the first ancestor of node I ). Assuming there are n nodes in total, we can easily see that the time complexity of I enumeration in this array is O (n ), the time complexity of j enumeration is much lower than that of O (logn ).
For a tree structure, we can process the f [] [] array in the O (nlogn) Time: (unlike normal, because the recursive formula only involves smaller j, only needs to enumerate j in the outer layer, and enumerate I in the inner layer)
For (int I = 1; I <= n; I ++) f [I] [0] = father [I]; for (int j = 1; j <= LOG; j ++) // here the LOG represents the constant log (n), usually around 20 for (int I = 1; I <= n; I ++) f [I] [j] = f [f [I] [J-1] [J-1];
This code should be better understood: f [I] [0] indicates the ancestor (2 ^ 0 = 1) of node I, that is, the father of I; node I 2nd ^ j ancestor, that is node I 2nd ^ (J-1) ancestor 2nd ^ (J-1) ancestor, because 2 ^ (J-1) + 2 ^ (J-1) = 2 ^ j. (Well, it may be a bit twisted to draw a picture ).
Then, how can we use the f [] [] array to quickly locate the k ancestor of node x? To multiply an array, you can only jump up to the integer power node of 2 at a time. We can split k into a binary number based on this property, and then use the power of 2 to generate k. The Code is as follows:
int getk(int x,int k){ for(int i=0;i<LOG;i++ if(k&(1<<i)) x=f[x][i]; return x;}
So what if the problem becomes that the depth of x is the ancestor of d? (Depth: if the first ancestor of a node is the root node, the depth of the node is a.) In fact, it is very simple. You just need to change it to the above problem:
int getd(int x,int d){ return getk(x,dep[x]-d);}
Recent common ancestor problems (LCA)
What is the recent common ancestor? For any two node u, v, u, and v with a root tree, the node with the largest depth (the farthest from the root) is called the closest common ancestor of u and v.
So how can we find the nearest common ancestor of two nodes? We can determine the path from the two nodes to the root node. For example, if we require the recent common ancestor of node 4 and node 5, we find that their paths to the root node are 4-3-2-1 and 5-2-1, respectively, so we can traverse these two paths from the back to the front simultaneously: their last path points are the same, and the last and second paths are the same, but they are different from the last and third paths, therefore, we use the last group of identical nodes as the closest public ancestor-node 2 is the closest public ancestor of node 4 and node 5. However, this process is a bit complicated: first we need to get the path from two nodes to the root node and then find it back, so we can optimize this process. Before calculating the path, we first agree to the depth of the two nodes: the depth of node 4 is 3, and the depth of node 5 is 2. So we move node 4 up to node 3, make the depth of the two nodes consistent, and then find the nearest common ancestor of Node 3 and node 5 (this operation will certainly not affect the result ). In this way, we perform path calculation on the two nodes at the same time. Their first pair of the same path points (still Node 2) is the closest common ancestor.
In fact, we can directly use a multiplier array to find the nearest common ancestor. (The following code is actually for the two nodes under the recent common ancestor. The recent common ancestor is father [u] And father [v])
for(i=LOG-1;i=0;i--) if(f[u][i]!=f[v][i]) u=f[u][i], v=f[v][i];
Before entering this code, we need to make a special judgment on u and v: if u = v, then this point itself is the most recent public ancestor. If u! = V, and then go to the Code solution.
Some template questions
Los Valley P3019 Meet Place meeting point (USACO 2011 Mar-Silver)
Codevs 4605 LCA
COGS 1588 Distance Queries Distance Consulting (USACO 2004 Feb-Gold)
Query the maximum PATH value [NOIP2013] freight car transportation
There are n cities numbered from 1 to n, and there are m two-way roads between the cities. Each road imposes weight limitations on vehicles. Currently, q trucks are transporting goods, and each vehicle has its own starting and ending cities. drivers want to know that each vehicle can carry up to multiple goods without exceeding the limit. (N, m, and q are considered to be 0.1 million)
Ideas:Find the maximum Spanning Tree of the graph. It is easy to know that the minimum path between the two points on the maximum spanning tree is equivalent to the weight of the bottleneck edge in the source image. Note that f [I] [j] indicates the 2nd ^ j ancestor of node I. Open an array g [I] [j] to indicate the minimum values of node I to 2nd ^ j ancestor .. The two arrays are obtained by recursion. In the process of calculating the LCA by multiplication. the minimum value of the synchronization record path is enough. That is to say, each time the x = f [x] [I] operation is executed, the synchronization record ans = min (ans, g [x] [I]). Time Complexity: 0 (n + m) logn ).
<This question is everywhere, so no code is posted.>
CQBZOJ (November 2015 Chongqing multi-school NOIP simulated Joint Entrance Exam)
Given a tree with n nodes, the vertex carries the weight m requests to find the number of the node with the first vertex weight not less than w from a to B, no output-1 (n ≤ 10 w, m ≤ 30 w)
Thought 1:If c = LCA (a, B) is set, a path is split into a-> c, c-> B. the advantage is that each part is a straight line of the first descendant of an ancestor, because it is required to be closest to. therefore, the original problem was split into two subtasks: the nearest/farthest weight from one point to one of its ancestors is not less than w. Note that f [I] [j] is the ancestor array, and g [I] [j] indicates the largest vertex right of the 2 ^ j ancestor closest to node I. Obviously, the two subtasks can be completed in binary depth. Complexity 0 (m1og ^ 2n) cannot pass all data.
Thought 2:Consider A straight line of x-y As A and B: A contains an integer power node of 2, and the length of A is greater than 1/2 of the total length of x-y; the rest is B. In this way, we can examine the above two subtasks:
I. Closest to x:
When k is known, we can use O (1) to determine whether the answer is in. ① If in A, enumerative I from K-1 to 0 to get the answer. ② If A does not have A legal solution, x = f [x] [k] is used for recursion. For ①, the entire recursive process is executed only once before the answer is obtained. For ②, the value of k is reduced after each recursion, while the upper bound of k is logn and the lower bound is 0. Therefore, the recursive depth is O (logn. The operation complexity is O (logn ).
2. the farthest from x:
① X = f [x] [k] is given priority, and recursion is carried out in section B. If the answer is obtained after recursive return, the answer is returned directly. ② If ① does not obtain the answer, O (1) checks whether there is an answer in section A. If not, there is no solution in the entire path of x-y. If so, enumerate I from K-1 to 0 to locate the answer. Similar to subtask 1, the complexity of this part is O (logn ).
Code
# Include <cstdio> # include <algorithm> # include <cmath> # include <cstdlib> # include <iostream> # include <cstring> using namespace std; # define MAXN 100010 # define LOG 18int Log, n, m, l [MAXN], f [MAXN] [2], MS [MAXN], t, a, B, c, x, y, qi, g [MAXN], s [MAXN], ans, fa [MAXN], p [MAXN] [LOG], mx [MAXN] [LOG]; void Read (int & x) {char c; while (c = getchar (), c! = EOF) if (c> = '0' & c <= '9') {x = c-'0'; while (c = getchar (), c >='0' & c <= '9') x = x * 10 + c-'0'; ungetc (c, stdin); return ;}} struct node {int v, wt; node * next;} edge [MAXN * 2], * adj [MAXN], * ecnt = edge; void add (int u, int v, int wt) {node * p = ++ ecnt; p-> v = v; p-> wt = wt; p-> next = adj [u]; adj [u] = p;} void dfs1 (int u) {int v; for (node * p = adj [u]; p = p-> next) {v = p-> v; if (v = fa [u]) continue; fa [v] = u; l [v] = l [u] + 1; dfs1 (v); if (f [v] [0] + P-> wt> f [u] [0]) {f [u] [1] = f [u] [0]; f [u] [0] = f [v] [0] + p-> wt; MS [u] = v ;} else if (f [v] [0] + p-> wt> f [u] [1]) f [u] [1] = f [v] [0] + p-> wt ;}} void dfs2 (int u, int wt) {if (MS [fa [u] = u) g [u] = f [fa [u] [1]; else g [u] = f [fa [u] [0]; g [u] = max (g [u], g [fa [u]) + wt; for (node * p = adj [u]; p = p-> next) if (p-> v! = Fa [u]) dfs2 (p-> v, p-> wt);} void prepare () {int I, j; for (I = 1; I <= n; I ++) {p [I] [0] = fa [I]; mx [I] [0] = s [fa [I];} for (j = 1; j <= Log; j ++) for (I = 1; I <= n; I ++) {p [I] [j] = p [p [I] [J-1] [J-1]; mx [I] [j] = max (mx [I] [J-1], mx [p [I] [J-1] [J-1]);} int lca (int x, int y) {int I; if (l [x] <l [y]) swap (x, y); for (I = Log; i> = 0; I --) if (l [x]-(1 <I)> = l [y]) x = p [x] [I]; if (x = y) return x; for (I = Log; I> = 0; I --) if (p [x] [I]! = P [y] [I]) x = p [x] [I], y = p [y] [I]; return fa [x];} int find1 (int x, int d, int a) {if (l [x] <= l [a]) return-1; (; l [p [x] [d] <l [a] & d> = 0; d --); if (mx [x] [d] <qi) return find1 (p [x] [d], D-1, a); for (int I = D-1; I> = 0; I --) if (mx [x] [I] <qi) x = p [x] [I]; return fa [x];} int find2 (int x, int d, int a) {int I, ret; if (l [x] <= l [a]) return-1; (; l [p [x] [d] <l [a] & d> = 0; d --); ret = find2 (p [x] [d], D-1, a); if (~ Ret) return ret; if (mx [x] [d] <qi) return-1; for (I = D-1; I> = 0; I --) if (mx [p [x] [I] [I]> = qi) x = p [x] [I]; return fa [x];} int main () {int I, u, v, wt; Read (n), Read (m), Read (a), Read (B), Read (c ); for (Log = 0; 1 <Log <= n; Log ++); Log --; for (I = 1; I <n; I ++) {Read (u), Read (v), Read (wt); add (u, v, wt); add (v, u, wt) ;} dfs1 (1 ); dfs2 (1, 0); for (I = 1; I <= n; I ++) s [I] = (long) max (f [I] [0], g [I]) + a) * B % c; prepare (); while (m --) {Read (x), Read (y), Read (qi ); int d, a = lca (x, y); ans =-1; if (s [x]> = qi) ans = x; if (ans =-1) {for (d = 0; 1 <d <= l [x]; d ++); d --; ans = find1 (x, d, );} if (ans =-1) {for (d = 0; 1 <d <= l [y]; d ++); d --; ans = find2 (y, d, a) ;}if (ans =-1 & s [y]> = qi) ans = y; printf ("% d \ n", ans );} return 0 ;}View Code and link generation
Before starting, we need to understand the following terms:
Son-heavy: For a Father's Day, the son has the largest tree.
Duplicate edges (the rough edges in the figure): the edges connecting Father's Day and son. Light Side: an edge other than the heavy side.
Heavy Chain: The chain formed by the heavy edge. Note that a node Without Duplicate EDGE connection contains only the heavy chain of the node itself (such as node 12 ).
There are also some marks that we will often mention:
Siz [x]: size of the subtree of node x, for example, siz [2] = 5
Son [x]: the son of node x, such as son [1] = 4
Top [x]: The top node of the heavy chain where node x is located, for example, top [11] = 2, top [12] = 12, top [13] = 1
So, how can we use code to implement the process of weight Chain Division? The following is the pseudocode:
Void dfs1 (u) {siz [u] = 1; for (v is the son of u) {dfs1 (v); siz [u] + = siz [v]; if (siz [v]> siz [son [u]) son [u] = v ;}} void dfs2 (u) {if (son [u]) top [son [u] = top [u], dfs2 (son [u]); for (v is the son of u and v! = Son [u]) {top [v] = v; dfs2 (v) ;}} int main () {dfsl (1), top [1] = 1, dfs2 (1 );}
In addition, we need to add a new nature: from any node to the root, the light side is O (logn) level (proof: the difference between the number of sub-tree points at both ends of the edge is at least two times ). We can even find that the constant level of the nlogn with the number of light edges is actually very small.
As a result, tree chain partitioning has a variety of applications:
Retrieve recent common ancestor
Define dep [x] to indicate the depth of x, for example, dep [1] = 1, dep [7] = 3.
int lca(int u,int v){ while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) u=fa[top[u]]; else v=fa[top[v]]; } return dep[u]<dep[v] ? u:v;}
Assume that v is the ancestor of u, and find the point that is the ancestor of u and the son of v.
int getlow(int u,int v){ while(top[u]!=top[v]) { if(fa[top[u]]==v) return top[u]; u=fa[top[u]]; } return son[v];}
Find the point whose depth is d in the ancestor of a vertex.
According to the Code Implementation of dfs2, it can be concluded that all points on the same heavy chain must be continuous in the dfs sequence of dfs2. Note dfn [x] indicates that node x is the number of nodes that have been traversed. Note that id [x] indicates the number of nodes that have been traversed by node x. In the dfs2 function, add the statement id [dfn [u] = ++ tmr] = u to the first line.
int find(int u,int d){ while(dep[top[u]]>d) u=fa[top[u]]; return id[dfn[u]-(dep[u]-d)];}
Examples
HDU 5452 Minimum Cut (2015 ACM/ICPC Asia Shenyang site network competition)
Codevs 4632 transportation plan (NOIP2015)
[Supplement] alternative bridge edge (CUT/cut edge) Algorithm
Cut edge definition: For an undirected graph, if the entire graph is completely separated after an edge is removed, the edge is called cut edge. The cut edge is not in any ring.
Save edges in any order, and then perform a query to query the Set Optimization spanning tree. All cut edges must exist in any spanning tree.
Access the array of saved edges again, and ignore the edges in the generated tree. The appearance of an edge will definitely generate a ring on the generation tree. The edge in this ring is obviously not a cut edge, so the number of overwrites of this path is + 1.
Obtain the subtree sum. The edge set with the number of covers 0 is the cut set.
Examples
BZOJ 3069 Hard Choice
BZOJ 4381 Odwiedziny
Summary
The chain section runs very fast and can directly replace tarjan offline lca.
|
Space complexity |
Preprocessing complexity |
Complexity of a single query |
Multiply |
O (nlogn) |
O (nlogn) |
O (logn) |
Chain Section |
O (n) |
O (n) |
O (nlogn) |
Another type of problem: Kruskal tree
(In the discussion about Kruskal, we take the minimal spanning tree as an example .)
We will use the Kruskal algorithm to obtain the minimum spanning tree: Sort edges by weight, add edges sequentially, and maintain connectivity with the query set.
Next, we will make some improvements to this algorithm: We will regard the edge as an additional vertex. When we merge two sets, we will take the vertex corresponding to the edge as the new ancestor, so that we will generate the Kruskal tree. The worst of its height is O (n), but we can also optimize it: we find that we actually only need to access its root node. Therefore, we can use and query set path compression for optimization.
The generated Kruskal tree has the following theorem:
Theorem 1: the Kruskal tree is a binary tree (because the edge must be connected to only two vertices). The leaf nodes in the tree are the original nodes, and the other nodes are the original edges.
Theorem 2: In the Minimum Spanning Tree example, the vertex weights increase progressively from the leaf to the root path.
Theorem 3: the weights of the two leaf nodes are the bottleneck edge between two points in the source image.
Examples
BZOJ 3551 Peaks
Codevs 3287 freight car transportation (NOIP2013)
The last example is the previous set.
(I cannot find the original question. I can only write a rough question)
There are n vertices. Each vertex is a set at the initial time. Two operations are supported: Merge the set of vertex x and vertex y. Query whether verbs a and B are in the same set before the k merge operation.
N ≤ 1000000, forced online
Ideas:
Use the Kruskal tree to set edge weight to the operation number. Because the edge weights increase sequentially according to the order of Edge Addition, it satisfies the nature of the Kruskal algorithm. When querying, if two different roots are found so far, the answer must be no. If the root is the same, the weights of the two points are queried. If the weights are smaller than k, the answer is yes; otherwise, the answer is no. The bottleneck edge between the two points is the biggest edge of the required weight from a to B. In this question, it corresponds to the last added edge.
Unfortunately, the Kruskal tree implemented by path compression cannot be used to quickly query LCA before being constructed. If offline operations are supported, you can perform all merge operations before querying. What are the bottlenecks of using the Kruskal tree? A new node must be created for each merge, resulting in uncontrollable height.
Creating a node with a value is equivalent to connecting an edge with a value between the two nodes. Grant the top right to the edge of the query set, indicating the number of added edges. By rank merge optimization, the tree height can be controlled at the log level. The maximum path of the two nodes in the query set is equal to the connection time of the two nodes, that is, the weights of the LCA in the Kruskal tree. And check whether the set height is O (logn). In this case, you can obtain the LCA brute force.
Optimization of concurrent query set in this question -- merge by rank
Directly merge and query sets without optimization. The complexity of a single operation may reach O (n ). When the edge of the query set has Edge Weight, path compression may cause loss of edge information.
Define and query the rank of the Set, and record the height of the tree with x as the root (when x is not the root, the value of rnk [x] is meaningless and will not be accessed ). When merging, use the root of the tree with a higher height as the new root.
The merge code is as follows:
int find(int x){ if(fa[x]==x) return x; return find(fa[x]);}void unite(int x,int y){ x=find(x),y=find(y); if(rnk[x]>rnk[y]) swap(x,y); fa[x]=y; if(rnk[x]==rnk[y]) rnk[y]++;}
It can be proved that O (logn) is the worst case for a single operation of the Combined Query set by rank ). Path compression and query set the worst complexity of a single operation is O (n). When the number of operations reaches O (n), the average complexity is O (logn ).
Time: