• 树链剖分-


    目录

    LCA

    树的统计


    LCA

     LCA问题,可以使用之前的倍增来解决,这里也可以用树链剖分来处理。

    树链剖分一般也叫做重链剖分(无特殊说明)。另一种是长链剖分。重链剖分跟重儿子的定义一样,也是节点数最多的。

    每一个节点都会有一条重边(也就是在孩子中找出一个节点数最多的子树。)这些重边就会形成一条重链。

    树链剖分的特点:

            

            每个点都会连接一条重链的,可能这条重链的长度只有1,可能这个点就是这条重链的顶端。           一段重链 —— 一条轻链 —— 一段重链 —— 一条轻链 —— 一段重链 ... 

    1. // problem :
    2. #include
    3. using namespace std;
    4. #define ll long long
    5. typedef pair<int, int> PII;
    6. #define pb push_back
    7. const int N = 101000;
    8. int sz[N], hs[N], fa[N], dep[N], id[N], l[N], r[N], top[N];
    9. std::vector<int> e[N];
    10. int tot, n, m;
    11. void dfs1(int u, int f) {
    12. sz[u] = 1;
    13. hs[u] = -1;
    14. fa[u] = f;
    15. dep[u] = dep[f] + 1;
    16. for (auto v : e[u]) {
    17. if (v == f) continue;
    18. dfs1(v, u);
    19. sz[u] += sz[v];
    20. if (hs[u] == -1 || sz[hs[u]] < sz[v])
    21. hs[u] = v;
    22. }
    23. }
    24. void dfs2(int u, int t) {
    25. top[u] = t;
    26. // l[u] = ++tot;
    27. // id[tot] = u;
    28. if (hs[u] != -1) {
    29. dfs2(hs[u], t);
    30. }
    31. for (auto v : e[u]) {
    32. if (v != fa[u] && v != hs[u])
    33. dfs2(v, v);
    34. }
    35. // r[u] = tot;
    36. }
    37. int LCA(int u, int v) {
    38. while (top[u] != top[v]) {
    39. if (dep[top[u]] < dep[top[v]]) v = fa[top[v]];
    40. else u = fa[top[u]];
    41. }
    42. if (dep[u] < dep[v]) return u;
    43. else return v;
    44. }
    45. int main(){
    46. scanf("%d", &n);
    47. for (int i = 1; i < n; ++i) {
    48. int u, v;
    49. scanf("%d %d", &u, &v);
    50. e[u].push_back(v);
    51. e[v].push_back(u);
    52. }
    53. dfs1(1, 0);
    54. dfs2(1, 1);
    55. scanf("%d", &m);
    56. for (int i = 1; i <= m; ++i) {
    57. int u, v;
    58. scanf("%d %d", &u, &v);
    59. printf("%d\n", LCA(u, v));
    60. }
    61. return 0;
    62. }

    树的统计

     将树链剖分、线段树结合在一起。模板题。   树链剖分 + DFS序(优先遍历重边,使得重边的序号是连续的),在DFS序的基础上,建线段树。

    要清楚DFS序中  l、r数组代表的含义,以及idx数组代表的意思。     

    idx[i] ,  第i个DFS序的所对应的节点编号,建线段树时赋初值用到。 

    l[u]  : u 节点对应的DFS序的左区间,同时也是第l[u]遍历到的点

    1. // problem :
    2. #include
    3. using namespace std;
    4. #define ll long long
    5. typedef pair<int, int> PII;
    6. #define pb push_back
    7. const int N = 101000;
    8. int n, m, a[N]; // 读入
    9. std::vector<int> e[N]; // 读入
    10. int sz[N], hs[N], fa[N], dep[N], top[N]; // 树链剖分
    11. int l[N], r[N], tot, idx[N]; // DFS序
    12. void dfs1(int u, int f) {
    13. sz[u] = 1;
    14. hs[u] = -1;
    15. fa[u] = f;
    16. dep[u] = dep[f] + 1;
    17. for (auto v : e[u]) {
    18. if (v == f) continue;
    19. dfs1(v, u);
    20. sz[u] += sz[v];
    21. if (hs[u] == -1 || sz[hs[u]] < sz[v])
    22. hs[u] = v;
    23. }
    24. }
    25. void dfs2(int u, int t) {
    26. top[u] = t;
    27. l[u] = ++tot;
    28. idx[tot] = u;
    29. if (hs[u] != -1) {
    30. dfs2(hs[u], t);
    31. }
    32. for (auto v : e[u]) {
    33. if (v != fa[u] && v != hs[u])
    34. dfs2(v, v);
    35. }
    36. r[u] = tot;
    37. }
    38. struct info {
    39. int maxv, sum;
    40. };
    41. info operator + (const info &l, const info &r) {
    42. info ans;
    43. ans.maxv = max(l.maxv, r.maxv);
    44. ans.sum = l.sum + r.sum;
    45. return ans;
    46. }
    47. struct node {
    48. info val;
    49. }seg[N * 4];
    50. void update(int id) {
    51. seg[id].val = seg[id * 2].val + seg[id * 2 + 1].val;
    52. }
    53. void build(int id, int l, int r){ // 基于DFS序建线段树,不是节点1-n
    54. if(l == r) {
    55. // L号点,DFS序中的第L个点
    56. seg[id].val = {a[idx[l]], a[idx[l]]};
    57. } else {
    58. int mid = (l + r) / 2;
    59. build(id * 2, l, mid);
    60. build(id * 2 + 1, mid + 1, r);
    61. update(id);
    62. }
    63. }
    64. void change(int id, int l, int r, int pos, int val) {
    65. if(l == r) {
    66. seg[id].val = {val, val};
    67. } else {
    68. int mid = (l + r) / 2;
    69. if(pos <= mid) change(id * 2, l, mid, pos, val);
    70. else change(id * 2 + 1, mid + 1, r, pos, val);
    71. update(id);
    72. }
    73. }
    74. info query(int id, int l, int r, int ql, int qr) {
    75. if(ql == l && qr == r) {
    76. return seg[id].val;
    77. }
    78. int mid = (l + r) / 2;
    79. if(qr <= mid) return query(id * 2, l, mid, ql, qr);
    80. else if(ql > mid) return query(id * 2 + 1, mid + 1, r, ql, qr);
    81. else return query(id * 2, l, mid, ql, mid) +
    82. query(id * 2 + 1, mid + 1, r, mid + 1, qr);
    83. }
    84. info query(int u, int v) {
    85. info ans {(int) -1e9, 0};
    86. while (top[u] != top[v]) {
    87. if (dep[top[u]] < dep[top[v]]) {
    88. ans = ans + query(1, 1, n, l[top[v]], l[v]);
    89. v = fa[top[v]];
    90. } else {
    91. ans = ans + query(1, 1, n, l[top[u]], l[u]);
    92. u = fa[top[u]];
    93. }
    94. }
    95. if (dep[u] <= dep[v]) ans = ans + query(1, 1, n, l[u], l[v]);
    96. else ans = ans + query(1, 1, n, l[v], l[u]);
    97. return ans;
    98. }
    99. int main(){
    100. scanf("%d", &n);
    101. for (int i = 1; i < n; ++i) {
    102. int u, v;
    103. scanf("%d %d", &u, &v);
    104. e[u].push_back(v);
    105. e[v].push_back(u);
    106. }
    107. for (int i = 1; i <= n; ++i)
    108. scanf("%d", &a[i]);
    109. dfs1(1, 0);
    110. dfs2(1, 1);
    111. build(1, 1, n);
    112. scanf("%d", &m);
    113. for (int i = 1; i <= m; ++i) {
    114. int u, v;
    115. static char op[10];
    116. scanf("%s%d %d", op, &u, &v);
    117. if (op[0] == 'C') {
    118. change(1, 1, n, l[u], v);
    119. } else {
    120. info ans = query(u, v);
    121. if (op[1] == 'M') printf("%d\n", ans.maxv);
    122. else printf("%d\n", ans.sum);
    123. }
    124. }
    125. return 0;
    126. }

  • 相关阅读:
    数据安全前言技术研究联邦学习
    9. seatunnel-incubating-2.1.2安装部署
    驱动开发4 使用字符设备驱动的分步实现编写LED驱动(LED亮灯)
    Hadoop环境搭建-单机、伪分布式、完全分布式
    深入了解Spring Boot Actuator
    扫雷 | C语言 | 简单易懂 | 扫雷相关知识点总结
    CentOS 7通过yum安装Docker和docker-compose
    我该如何入门Python机器学习?
    Linux发展史
    docker环境,ubuntu18.04安装VTK8.2和PCL1.9.1
  • 原文地址:https://blog.csdn.net/PURSUE__LIFE/article/details/126094530