📜  重光分解|套装2(实施)

📅  最后修改于: 2021-04-17 11:12:24             🧑  作者: Mango

我们强烈建议您参考下面的帖子,以此作为前提条件。
重光分解|设置1(简介)
在上面的文章中,我们借助以下示例讨论了重轻分解(HLD)。
假设我们有一个n个节点的不平衡树(不一定是二叉树) ,并且我们必须对该树执行操作以回答许多查询,每个查询可以是以下两种类型之一:

  1. 变化(A,B):a边缘至b的更新权重。
  2. maxEdge(a,b) :在从节点a到节点b的路径上打印最大边缘权重。例如maxEdge(5,10)应该打印25。

本文讨论了相同的实现
我们针对此问题的攻击路线是:

  1. 创建树
  2. 为每个节点设置子树的大小,深度和父级(使用DFS)
  3. 将树分解成不相交的链
  4. 建立细分树
  5. 回答查询

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的索引)。

Newhld5

5.回答查询
在上一篇文章中,我们已经详细讨论了mexEdge()查询。对于maxEdge(u,v),我们找到从u到LCA,从v到LCA的路径上的最大权重边,并返回最大值2。
对于change()查询,我们可以使用要更新权重的边缘的最深端来更新分段树。我们将找到数组中边缘最深处的位置,作为片段树的基础,然后从该节点开始更新,然后向上移动更新片段树。假设我们要将边缘8(在节点7和节点9之间)更新为28。更深的节点9在基本数组中的位置为10,我们这样做如下:

hld9

以下是上述步骤的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