• 倍增、DFS序


     

    目录

    路径最小值​

    DFS序练习1

    DFS序练习2


    路径最小值

     

     

     

    dfs,相当于一个初始化操作;

    以及有一个跟ST表类似的预处理操作

    query函数的总体思想就是,利用2个节点的深度来不断向上查最近公共祖先。(前提是2个节点处于同一深度,代码中有体现这一点,可能处于同一深度之后,u就是v的祖先,也可能不是,那就继续向上查找)

    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 LOGN = 18;
    8. const int N = 201000;
    9. int n, q;
    10. int dep[N], par[N][LOGN + 1], val[N][LOGN + 1];
    11. std::vectorint, int>> e[N];
    12. int query(int u, int v) {
    13. int ans = 1 << 30;
    14. if(dep[u] > dep[v]) swap(u, v);
    15. int d = dep[v] - dep[u];
    16. for (int j = LOGN; j >= 0; --j) if (d & (1 << j)) {
    17. ans = min(ans, val[v][j]);
    18. v = par[v][j];
    19. }
    20. if (u == v) return ans;
    21. for (int j = LOGN; j >= 0; --j) if (par[u][j] != par[v][j]) {
    22. ans = min(ans, min(val[u][j], val[v][j]));
    23. u = par[u][j];
    24. v = par[v][j];
    25. }
    26. ans = min(ans, min(val[u][0], val[v][0]));
    27. return ans;
    28. }
    29. void dfs(int u, int f) {
    30. dep[u] = dep[f] + 1;
    31. for (auto p : e[u]) {
    32. int v = p.first;
    33. if (v == f) continue;
    34. par[v][0] = u;
    35. val[v][0] = p.second;
    36. dfs(v, u);
    37. }
    38. }
    39. int main(){
    40. scanf("%d %d", &n, &q);
    41. for (int i = 1; i < n; ++i) {
    42. int u, v, w;
    43. scanf("%d %d %d", &u, &v, &w);
    44. e[u].push_back(make_pair(v, w));
    45. e[v].push_back(make_pair(u, w));
    46. }
    47. dfs(1, 0);
    48. for (int j = 1; j <= LOGN; ++j) {
    49. for (int u = 1; u <= n; ++u) {
    50. par[u][j] = par[par[u][j - 1]][j - 1];
    51. val[u][j] = min(val[u][j - 1], val[par[u][j - 1]][j - 1]);
    52. }
    53. }
    54. for (int i = 1; i <= q; ++i) {
    55. int u, v;
    56. scanf("%d %d", &u, &v);
    57. printf("%d\n", query(u, v));
    58. }
    59. return 0;
    60. }

    DFS序练习1

     

     

    DFS序,   将一棵树有序化,特点是子树的序号是连续的,(子树 相当于 涉及区间问题)

    c1  维护区间和   

    c2 维护点x到根的点权和     (差分) 

    为什么为想到差分呢? 可以知道,点x的孩子要到根的话,那么一定会经过点x,所以要加上x这个点的点权, 所以是区间加,并且是单点查询,很自然就想到差分树状数组了。

    1. // problem : DFS序
    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 = 201000;
    8. int n, q, l[N], r[N], tot, a[N];
    9. std::vector<int> e[N];
    10. template<class T>
    11. struct BIT{
    12. T c[N];
    13. int size;
    14. void init(int n){
    15. size = n;
    16. for (int i = 1; i <= n; ++i) c[i] = 0;
    17. }
    18. inline void modify(int x, T d){
    19. while(x <= size){
    20. c[x] += d;
    21. x += x & -x;
    22. }
    23. }
    24. inline T query(int x){
    25. T res = 0;
    26. while(x){
    27. res += c[x];
    28. x -= x & -x;
    29. }
    30. return res;
    31. }
    32. };
    33. BIT c1, c2;
    34. void dfs(int u, int f) {
    35. l[u] = ++tot;
    36. for (auto v : e[u]) {
    37. if (v == f) continue;
    38. dfs(v, u);
    39. }
    40. r[u] = tot;
    41. }
    42. int main(){
    43. scanf("%d %d", &n, &q);
    44. for (int i = 1; i < n; ++i) {
    45. int u, v;
    46. scanf("%d %d", &u, &v);
    47. e[u].push_back(v);
    48. e[v].push_back(u);
    49. }
    50. dfs(1, 0);
    51. c1.init(n);
    52. c2.init(n);
    53. for (int i = 1; i <= n; ++i) {
    54. scanf("%d", &a[i]);
    55. c1.modify(l[i], a[i]);
    56. c2.modify(l[i], a[i]);
    57. c2.modify(r[i] + 1, -a[i]);
    58. }
    59. for (int i = 1; i <= q; ++i) {
    60. int ty;
    61. scanf("%d", &ty);
    62. if (ty == 1) {
    63. int x, y;
    64. scanf("%d %d", &x, &y);
    65. int d = y - a[x];
    66. a[x] = y;
    67. c1.modify(l[x], d);
    68. c2.modify(l[x], d);
    69. c2.modify(r[x] + 1, -d);
    70. } else {
    71. int x;
    72. scanf("%d", &x);
    73. printf("%lld %lld\n", c1.query(r[x]) - c1.query(l[x] - 1), c2.query(l[x]));
    74. }
    75. }
    76. return 0;
    77. }

     

    DFS序练习2

     这题跟上一题没有区别,唯一变动的地方就是多了个换根操作,这也是难点之一。

    我们一旦换了根,那我们原先的DFS序就会被打乱。这里的解决方法是不进行真正的换根操作,而进行逻辑上的换根。当我们查询x点子树的点权和时,通过x和root之间的关系,来进行模拟换根处理。

    这里x和root之间有3种关系:

            1、x 就是 root          ---->  query(n)

            2、root是x的子孙     整体减去 x 的包含root区间的那个子区间

            3、其他                   还是正常的query(r[x]) - query(l[x] - 1)

    第1种情况很明显,不画图演示。

    第2种情况: 

     

     第3种情况:

     

    1. // problem : DFS序
    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 = 201000;
    8. int n, q, l[N], r[N], tot, a[N];
    9. std::vector<int> e[N];
    10. vector son[N];
    11. template<class T>
    12. struct BIT{
    13. T c[N];
    14. int size;
    15. void init(int n){
    16. size = n;
    17. for (int i = 1; i <= n; ++i) c[i] = 0;
    18. }
    19. inline void modify(int x, T d){
    20. while(x <= size){
    21. c[x] += d;
    22. x += x & -x;
    23. }
    24. }
    25. inline T query(int x){
    26. T res = 0;
    27. while(x){
    28. res += c[x];
    29. x -= x & -x;
    30. }
    31. return res;
    32. }
    33. };
    34. BIT c1;
    35. void dfs(int u, int f) {
    36. l[u] = ++tot;
    37. for (auto v : e[u]) {
    38. if (v == f) continue;
    39. dfs(v, u);
    40. son[u].push_back(make_pair(l[v], r[v]));
    41. }
    42. r[u] = tot;
    43. }
    44. int main(){
    45. scanf("%d %d", &n, &q);
    46. for (int i = 1; i < n; ++i) {
    47. int u, v;
    48. scanf("%d %d", &u, &v);
    49. e[u].push_back(v);
    50. e[v].push_back(u);
    51. }
    52. dfs(1, 0);
    53. c1.init(n);
    54. for (int i = 1; i <= n; ++i) {
    55. scanf("%d", &a[i]);
    56. c1.modify(l[i], a[i]);
    57. }
    58. int root = 1;
    59. for (int i = 1; i <= q; ++i) {
    60. int ty;
    61. scanf("%d", &ty);
    62. if (ty == 1) {
    63. int x, y;
    64. scanf("%d %d", &x, &y);
    65. int d = y - a[x];
    66. a[x] = y;
    67. c1.modify(l[x], d);
    68. } else if (ty == 3){
    69. scanf("%d", &root);
    70. } else {
    71. int x;
    72. scanf("%d", &x);
    73. if (x == root) {
    74. printf("%lld\n", c1.query(n));
    75. } else if (l[x] < l[root] && r[x] >= r[root]) {
    76. auto seg = *prev(upper_bound(son[x].begin(), son[x].end(),
    77. PII {l[root], r[root]}));
    78. printf("%lld\n", c1.query(n) - (c1.query(seg.second) - c1.query(seg.first - 1)));
    79. } else {
    80. printf("%lld\n", c1.query(r[x]) - c1.query(l[x] - 1));
    81. }
    82. }
    83. }
    84. return 0;
    85. }

  • 相关阅读:
    大数据-Spark-Spark开发高频面试题
    Linux内核之mutex互斥锁机制
    Docker手把手教程(七)公有云 & 核心技术
    Triton推理服务器吞吐量测试
    GitHub霸榜月余的24万字Java面试手册,竟是如此牛逼
    QTcpSocket发送结构体的做法
    torch F.unfold()举例
    代码随想录 | Day46
    【数据代理+事件处理+计算属性与监视+绑定样式+条件渲染】
    NPDP值得产品经理学习吗?
  • 原文地址:https://blog.csdn.net/PURSUE__LIFE/article/details/126075707