我们强烈建议您参考下面的帖子,以此作为前提条件。
重光分解|设置1(简介)
在上面的文章中,我们借助以下示例讨论了重轻分解(HLD)。
假设我们有一个n个节点的不平衡树(不一定是二叉树) ,并且我们必须对该树执行操作以回答许多查询,每个查询可以是以下两种类型之一:
- 变化(A,B):第a个边缘至b的更新权重。
- maxEdge(a,b) :在从节点a到节点b的路径上打印最大边缘权重。例如maxEdge(5,10)应该打印25。
本文讨论了相同的实现
我们针对此问题的攻击路线是:
- 创建树
- 为每个节点设置子树的大小,深度和父级(使用DFS)
- 将树分解成不相交的链
- 建立细分树
- 回答查询
1.树的创建:为了便于理解,实现使用树的邻接矩阵表示。可以使用邻接列表rep,并对源进行一些更改。如果在节点u和v之间存在权重为w的边数e,我们将e存储在tree [u] [v]和tree [v] [u]处,权重w分别存储在边权重的线性数组中(n- 1个边缘)。
2.设置每个节点的子树大小,深度和父级:接下来,我们在树上执行DFS,以设置存储每个节点的父树,子树大小和深度的数组。在进行DFS时,我们要做的另一件重要事情是存储我们遍历的每个边缘的更深节点。这将在更新树(change()查询)时为我们提供帮助。
3&4。将树分解为不相交的链,然后构建建筑物树现在是最重要的部分:HLD。当我们遍历边缘并到达节点(从根开始)时,将边缘放置在段树的基础上,我们决定该节点将是新链的头部(如果是正常子代)还是当前链扩展(特殊子级),存储节点所属的链ID,并将其位置存储在段树库中(以供将来查询)。构建段树的基础,以使属于同一链的所有边都在一起,并且链之间用浅色边分开。
插图:我们从节点1开始。由于没有到达该节点的任何边缘,因此我们在数组中插入“ -1”作为虚边的权重,该数组将作为段树的基础。
接下来,我们移至节点1的特殊子节点,即节点2,并且由于遍历了权重为13的边,因此将13添加到基本数组中。节点2的特殊子节点为节点6。我们遍历权重为25的边到达节点6。我们将其插入基本数组。同样,我们还没有到达叶节点(在本例中为节点10)时,就进一步扩展了该链。
然后,我们移到最后一个叶节点的父级的正常子级,并标记新链的开始。这里的父级是节点8,正常的子级是节点11。我们遍历权重为6的边并将其插入基本数组。这就是我们完成细分树的基础数组的方式。
还要记住,我们需要将每个节点的位置存储在段树中,以备将来查询。节点1的位置是1,节点2是2,节点6是3,节点8是4,…,节点11是6,节点5是7,节点9是10,节点4是11(基于1的索引)。
5.回答查询
在上一篇文章中,我们已经详细讨论了mexEdge()查询。对于maxEdge(u,v),我们找到从u到LCA,从v到LCA的路径上的最大权重边,并返回最大值2。
对于change()查询,我们可以使用要更新权重的边缘的最深端来更新分段树。我们将找到数组中边缘最深处的位置,作为片段树的基础,然后从该节点开始更新,然后向上移动更新片段树。假设我们要将边缘8(在节点7和节点9之间)更新为28。更深的节点9在基本数组中的位置为10,我们这样做如下:
以下是上述步骤的C++实现。
CPP
/* C++ program for Heavy-Light Decomposition of a tree */
#include
using namespace std;
#define N 1024
int tree[N][N]; // Matrix representing the tree
/* a tree node structure. Every node has a parent, depth,
subtree size, chain to which it belongs and a position
in base array*/
struct treeNode
{
int par; // Parent of this node
int depth; // Depth of this node
int size; // Size of subtree rooted with this node
int pos_segbase; // Position in segment tree base
int chain;
} node[N];
/* every Edge has a weight and two ends. We store deeper end */
struct Edge
{
int weight; // Weight of Edge
int deeper_end; // Deeper end
} edge[N];
/* we construct one segment tree, on base array */
struct segmentTree
{
int base_array[N], tree[6*N];
} s;
// A function to add Edges to the Tree matrix
// e is Edge ID, u and v are the two nodes, w is weight
void addEdge(int e, int u, int v, int w)
{
/*tree as undirected graph*/
tree[u-1][v-1] = e-1;
tree[v-1][u-1] = e-1;
edge[e-1].weight = w;
}
// A recursive function for DFS on the tree
// curr is the current node, prev is the parent of curr,
// dep is its depth
void dfs(int curr, int prev, int dep, int n)
{
/* set parent of current node to predecessor*/
node[curr].par = prev;
node[curr].depth = dep;
node[curr].size = 1;
/* for node's every child */
for (int j=0; j x || se <= x);
else if(ss == x && ss == se-1)s.tree[si] = val;
else
{
int mid = (ss + se)/2;
s.tree[si] = max(update_ST(ss, mid, si*2, x, val),
update_ST(mid, se, si*2+1, x, val));
}
return s.tree[si];
}
// A function to update Edge e's value to val in segment tree
void change(int e, int val, int n)
{
update_ST(0, n, 1, node[edge[e].deeper_end].pos_segbase, val);
// following lines of code make no change to our case as we are
// changing in ST above
// Edge_weights[e] = val;
// segtree_Edges_weights[deeper_end_of_edge[e]] = val;
}
// A function to get the LCA of nodes u and v
int LCA(int u, int v, int n)
{
/* array for storing path from u to root */
int LCA_aux[n+5];
// Set u is deeper node if it is not
if (node[u].depth < node[v].depth)
swap(u, v);
/* LCA_aux will store path from node u to the root*/
memset(LCA_aux, -1, sizeof(LCA_aux));
while (u!=-1)
{
LCA_aux[u] = 1;
u = node[u].par;
}
/* find first node common in path from v to root and u to
root using LCA_aux */
while (v)
{
if (LCA_aux[v]==1)break;
v = node[v].par;
}
return v;
}
/* A recursive function to get the minimum value in a given range
of array indexes. The following are parameters for this function.
st --> Pointer to segment tree
index --> Index of current node in the segment tree. Initially
0 is passed as root is always at index 0
ss & se --> Starting and ending indexes of the segment represented
by current node, i.e., st[index]
qs & qe --> Starting and ending indexes of query range */
int RMQUtil(int ss, int se, int qs, int qe, int index)
{
//printf("%d,%d,%d,%d,%d\n", ss, se, qs, qe, index);
// If segment of this node is a part of given range, then return
// the min of the segment
if (qs <= ss && qe >= se-1)
return s.tree[index];
// If segment of this node is outside the given range
if (se-1 < qs || ss > qe)
return -1;
// If a part of this segment overlaps with the given range
int mid = (ss + se)/2;
return max(RMQUtil(ss, mid, qs, qe, 2*index),
RMQUtil(mid, se, qs, qe, 2*index+1));
}
// Return minimum of elements in range from index qs (query start) to
// qe (query end). It mainly uses RMQUtil()
int RMQ(int qs, int qe, int n)
{
// Check for erroneous input values
if (qs < 0 || qe > n-1 || qs > qe)
{
printf("Invalid Input");
return -1;
}
return RMQUtil(0, n, qs, qe, 1);
}
// A function to move from u to v keeping track of the maximum
// we move to the surface changing u and chains
// until u and v donot belong to the same
int crawl_tree(int u, int v, int n, int chain_heads[])
{
int chain_u, chain_v = node[v].chain, ans = 0;
while (true)
{
chain_u = node[u].chain;
/* if the two nodes belong to same chain,
* we can query between their positions in the array
* acting as base to the segment tree. After the RMQ,
* we can break out as we have no where further to go */
if (chain_u==chain_v)
{
if (u==v); //trivial
else
ans = max(RMQ(node[v].pos_segbase+1, node[u].pos_segbase, n),
ans);
break;
}
/* else, we query between node u and head of the chain to which
u belongs and later change u to parent of head of the chain
to which u belongs indicating change of chain */
else
{
ans = max(ans,
RMQ(node[chain_heads[chain_u]].pos_segbase,
node[u].pos_segbase, n));
u = node[chain_heads[chain_u]].par;
}
}
return ans;
}
// A function for MAX_EDGE query
void maxEdge(int u, int v, int n, int chain_heads[])
{
int lca = LCA(u, v, n);
int ans = max(crawl_tree(u, lca, n, chain_heads),
crawl_tree(v, lca, n, chain_heads));
printf("%d\n", ans);
}
// driver function
int main()
{
/* fill adjacency matrix with -1 to indicate no connections */
memset(tree, -1, sizeof(tree));
int n = 11;
/* arguments in order: Edge ID, node u, node v, weight w*/
addEdge(1, 1, 2, 13);
addEdge(2, 1, 3, 9);
addEdge(3, 1, 4, 23);
addEdge(4, 2, 5, 4);
addEdge(5, 2, 6, 25);
addEdge(6, 3, 7, 29);
addEdge(7, 6, 8, 5);
addEdge(8, 7, 9, 30);
addEdge(9, 8, 10, 1);
addEdge(10, 8, 11, 6);
/* our tree is rooted at node 0 at depth 0 */
int root = 0, parent_of_root=-1, depth_of_root=0;
/* a DFS on the tree to set up:
* arrays for parent, depth, subtree size for every node;
* deeper end of every Edge */
dfs(root, parent_of_root, depth_of_root, n);
int chain_heads[N];
/*we have initialized no chain heads */
memset(chain_heads, -1, sizeof(chain_heads));
/* Stores number of edges for construction of segment
tree. Initially we haven't traversed any Edges. */
int edge_counted = 0;
/* we start with filling the 0th chain */
int curr_chain = 0;
/* HLD of tree */
hld(root, n-1, &edge_counted, &curr_chain, n, chain_heads);
/* ST of segregated Edges */
construct_ST(0, edge_counted, 1);
/* Since indexes are 0 based, node 11 means index 11-1,
8 means 8-1, and so on*/
int u = 11, v = 9;
cout << "Max edge between " << u << " and " << v << " is ";
maxEdge(u-1, v-1, n, chain_heads);
// Change value of edge number 8 (index 8-1) to 28
change(8-1, 28, n);
cout << "After Change: max edge between " << u << " and "
<< v << " is ";
maxEdge(u-1, v-1, n, chain_heads);
v = 4;
cout << "Max edge between " << u << " and " << v << " is ";
maxEdge(u-1, v-1, n, chain_heads);
// Change value of edge number 5 (index 5-1) to 22
change(5-1, 22, n);
cout << "After Change: max edge between " << u << " and "
<< v << " is ";
maxEdge(u-1, v-1, n, chain_heads);
return 0;
}
Java
/* Java program for Heavy-Light Decomposition of a tree */
import java.util.*;
class GFG
{
static final int N = 1024;
static int edge_counted;
static int curr_chain;
static int [][]tree = new int[N][N]; // Matrix representing the tree
/* a tree node structure. Every node has a parent, depth,
subtree size, chain to which it belongs and a position
in base array*/
static class treeNode
{
int par; // Parent of this node
int depth; // Depth of this node
int size; // Size of subtree rooted with this node
int pos_segbase; // Position in segment tree base
int chain;
} ;
static treeNode node[] = new treeNode[N];
/* every Edge has a weight and two ends. We store deeper end */
static class Edge
{
int weight; // Weight of Edge
int deeper_end; // Deeper end
Edge(int weight, int deeper_end)
{
this.weight = weight;
this.deeper_end = deeper_end;
}
} ;
static Edge edge[] = new Edge[N];
/* we conone segment tree, on base array */
static class segmentTree
{
int []base_array = new int[N];
int []tree = new int[6*N];
} ;
static segmentTree s = new segmentTree();
// A function to add Edges to the Tree matrix
// e is Edge ID, u and v are the two nodes, w is weight
static void addEdge(int e, int u, int v, int w)
{
/*tree as undirected graph*/
tree[u - 1][v - 1] = e - 1;
tree[v - 1][u - 1] = e - 1;
edge[e - 1].weight = w;
}
// A recursive function for DFS on the tree
// curr is the current node, prev is the parent of curr,
// dep is its depth
static void dfs(int curr, int prev, int dep, int n)
{
/* set parent of current node to predecessor*/
node[curr].par = prev;
node[curr].depth = dep;
node[curr].size = 1;
/* for node's every child */
for (int j = 0; j < n; j++)
{
if (j != curr && j != node[curr].par && tree[curr][j] != -1)
{
/* set deeper end of the Edge as this child*/
edge[tree[curr][j]].deeper_end = j;
/* do a DFS on subtree */
dfs(j, curr, dep + 1, n);
/* update subtree size */
node[curr].size += node[j].size;
}
}
}
// A recursive function that decomposes the Tree into chains
static void hld(int curr_node, int id,
int n, int chain_heads[])
{
/* if the current chain has no head, this node is the first node
* and also chain head */
if (chain_heads[curr_chain] == -1)
chain_heads[curr_chain] = curr_node;
/* set chain ID to which the node belongs */
node[curr_node].chain = curr_chain;
/* set position of node in the array acting as the base to
the segment tree */
node[curr_node].pos_segbase = edge_counted;
/* update array which is the base to the segment tree */
s.base_array[(edge_counted)++] = edge[id].weight;
/* Find the special child (child with maximum size) */
int spcl_chld = -1, spcl_edg_id = 0;
for (int j = 0; j < n; j++)
if (j != curr_node && j != node[curr_node].par && tree[curr_node][j] != -1)
if (spcl_chld == -1 || node[spcl_chld].size < node[j].size)
{
spcl_chld = j; spcl_edg_id = tree[curr_node][j];
}
/* if special child found, extend chain */
if (spcl_chld != -1)
hld(spcl_chld, spcl_edg_id, n, chain_heads);
/* for every other (normal) child, do HLD on child subtree as separate
chain*/
for (int j = 0; j < n; j++)
{
if (j != curr_node && j != node[curr_node].par &&
j != spcl_chld && tree[curr_node][j] != -1)
{
(curr_chain)++;
hld(j, tree[curr_node][j], n, chain_heads);
}
}
}
// A recursive function that constructs Segment Tree for array[ss..se).
// si is index of current node in segment tree st
static int construct_ST(int ss, int se, int si)
{
// If there is one element in array, store it in current node of
// segment tree and return
if (ss == se - 1)
{
s.tree[si] = s.base_array[ss];
return s.base_array[ss];
}
// If there are more than one elements, then recur for left and
// right subtrees and store the minimum of two values in this node
int mid = (ss + se) / 2;
s.tree[si] = Math.max(construct_ST(ss, mid, si * 2),
construct_ST(mid, se, si * 2 + 1));
return s.tree[si];
}
// A recursive function that updates the Segment Tree
// x is the node to be updated to value val
// si is the starting index of the segment tree
// ss, se mark the corners of the range represented by si
static int update_ST(int ss, int se, int si, int x, int val)
{
if(ss > x || se <= x);
else if(ss == x && ss == se - 1)s.tree[si] = val;
else
{
int mid = (ss + se) / 2;
s.tree[si] = Math.max(update_ST(ss, mid, si * 2, x, val),
update_ST(mid, se, si * 2 + 1, x, val));
}
return s.tree[si];
}
// A function to update Edge e's value to val in segment tree
static void change(int e, int val, int n)
{
update_ST(0, n, 1, node[edge[e].deeper_end].pos_segbase, val);
// following lines of code make no change to our case as we are
// changing in ST above
// Edge_weights[e] = val;
// segtree_Edges_weights[deeper_end_of_edge[e]] = val;
}
// A function to get the LCA of nodes u and v
static int LCA(int u, int v, int n)
{
/* array for storing path from u to root */
int []LCA_aux= new int[n + 5];
// Set u is deeper node if it is not
if (node[u].depth < node[v].depth)
{
int t = u;
u = v;
v = t;
}
/* LCA_aux will store path from node u to the root*/
Arrays.fill(LCA_aux, -1);
while (u != -1)
{
LCA_aux[u] = 1;
u = node[u].par;
}
/* find first node common in path from v to root and u to
root using LCA_aux */
while (v > 0)
{
if (LCA_aux[v] == 1)break;
v = node[v].par;
}
return v;
}
/* A recursive function to get the minimum value in a given range
of array indexes. The following are parameters for this function.
st -. Pointer to segment tree
index -. Index of current node in the segment tree. Initially
0 is passed as root is always at index 0
ss & se -. Starting and ending indexes of the segment represented
by current node, i.e., st[index]
qs & qe -. Starting and ending indexes of query range */
static int RMQUtil(int ss, int se, int qs, int qe, int index)
{
//System.out.printf("%d,%d,%d,%d,%d\n", ss, se, qs, qe, index);
// If segment of this node is a part of given range, then return
// the min of the segment
if (qs <= ss && qe >= se - 1)
return s.tree[index];
// If segment of this node is outside the given range
if (se - 1 < qs || ss > qe)
return -1;
// If a part of this segment overlaps with the given range
int mid = (ss + se)/2;
return Math.max(RMQUtil(ss, mid, qs, qe, 2 * index),
RMQUtil(mid, se, qs, qe, 2 * index + 1));
}
// Return minimum of elements in range from index qs (query start) to
// qe (query end). It mainly uses RMQUtil()
static int RMQ(int qs, int qe, int n)
{
// Check for erroneous input values
if (qs < 0 || qe > n-1 || qs > qe)
{
System.out.printf("Invalid Input");
return -1;
}
return RMQUtil(0, n, qs, qe, 1);
}
// A function to move from u to v keeping track of the maximum
// we move to the surface changing u and chains
// until u and v donot belong to the same
static int crawl_tree(int u, int v, int n, int chain_heads[])
{
int chain_u, chain_v = node[v].chain, ans = 0;
while (true)
{
chain_u = node[u].chain;
/* if the two nodes belong to same chain,
* we can query between their positions in the array
* acting as base to the segment tree. After the RMQ,
* we can break out as we have no where further to go */
if (chain_u == chain_v)
{
if (u == v); //trivial
else
ans = Math.max(RMQ(node[v].pos_segbase + 1, node[u].pos_segbase, n),
ans);
break;
}
/* else, we query between node u and head of the chain to which
u belongs and later change u to parent of head of the chain
to which u belongs indicating change of chain */
else
{
ans = Math.max(ans,
RMQ(node[chain_heads[chain_u]].pos_segbase,
node[u].pos_segbase, n));
u = node[chain_heads[chain_u]].par;
}
}
return ans;
}
// A function for MAX_EDGE query
static void maxEdge(int u, int v, int n, int chain_heads[])
{
int lca = LCA(u, v, n);
int ans = Math.max(crawl_tree(u, lca, n, chain_heads),
crawl_tree(v, lca, n, chain_heads));
System.out.printf("%d\n", ans);
}
// driver function
public static void main(String[] args)
{
/* fill adjacency matrix with -1 to indicate no connections */
for(int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
tree[i][j] = -1;
}
}
int n = 11;
for(int i = 0; i < edge.length; i++)
{
edge[i] = new Edge(0, 0);
}
for(int i = 0; i < node.length; i++)
{
node[i] = new treeNode();
}
/* arguments in order: Edge ID, node u, node v, weight w*/
addEdge(1, 1, 2, 13);
addEdge(2, 1, 3, 9);
addEdge(3, 1, 4, 23);
addEdge(4, 2, 5, 4);
addEdge(5, 2, 6, 25);
addEdge(6, 3, 7, 29);
addEdge(7, 6, 8, 5);
addEdge(8, 7, 9, 30);
addEdge(9, 8, 10, 1);
addEdge(10, 8, 11, 6);
/* our tree is rooted at node 0 at depth 0 */
int root = 0, parent_of_root = -1, depth_of_root = 0;
/* a DFS on the tree to set up:
* arrays for parent, depth, subtree size for every node;
* deeper end of every Edge */
dfs(root, parent_of_root, depth_of_root, n);
int []chain_heads = new int[N];
/*we have initialized no chain heads */
Arrays.fill(chain_heads, -1);
/* Stores number of edges for construction of segment
tree. Initially we haven't traversed any Edges. */
edge_counted = 0;
/* we start with filling the 0th chain */
curr_chain = 0;
/* HLD of tree */
hld(root, n - 1, n, chain_heads);
/* ST of segregated Edges */
construct_ST(0, edge_counted, 1);
/* Since indexes are 0 based, node 11 means index 11-1,
8 means 8-1, and so on*/
int u = 11, v = 9;
System.out.print("Max edge between " + u + " and " + v + " is ");
maxEdge(u - 1, v - 1, n, chain_heads);
// Change value of edge number 8 (index 8-1) to 28
change(8 - 1, 28, n);
System.out.print("After Change: max edge between " + u + " and "
+ v + " is ");
maxEdge(u - 1, v - 1, n, chain_heads);
v = 4;
System.out.print("Max edge between " + u + " and " + v + " is ");
maxEdge(u - 1, v - 1, n, chain_heads);
// Change value of edge number 5 (index 5-1) to 22
change(5 - 1, 22, n);
System.out.print("After Change: max edge between " + u + " and "
+ v + " is ");
maxEdge(u - 1, v - 1, n, chain_heads);
}
}
// This code contributed by Rajput-Ji
输出:
Max edge between 11 and 9 is 30
After Change: max edge between 11 and 9 is 29
Max edge between 11 and 4 is 25
After Change: max edge between 11 and 4 is 23