zoukankan      html  css  js  c++  java
  • 树链剖分模板

    LuoguP3384

    对于这个树剖吧。。。一开始不打算学的。。。但是貌似挺有用的,于是乎下狠心学了一哈。本蒟蒻初学树剖可能理解不太透彻,如有不对之处还请各位巨佬指正


     先来一个树剖的了解  IvanovCraft的博客

    首先我们需要了解一下树链剖分可以用来干什么:

    1.可以求树上差分,LCA(时间复杂度会小一些)

    2.最重要的是可以修改树上的边权或者点权(比如本题)

    剩下的目前本蒟蒻还too vegetable,所以对其理解不够深,许多用法可能以后会更新

    貌似大佬们看看上面博客就好了

    Code:

      1 #include <bits/stdc++.h>
      2 #define ls x << 1
      3 #define rs x << 1 | 1
      4 using namespace std;
      5 const int N = 200001;
      6 int n, m, r, p;
      7 int tot, head[N];//邻接链表存边 
      8 struct edge{
      9     int net, v;
     10 }e[N];
     11 int a[N * 4], v[N * 4];
     12 int cnt, w[N], wt[N], son[N], d[N], siz[N], fa[N], id[N], top[N];
     13 //w[]存读入的点,wt[]存dfs遍历后的点,son[]存每个节点的重儿子,d[]存每个点的深度,size[]存以当前节点为根节点的子树的大小
     14 //fa[]存每个节点的父亲节点,id[]存每个点的dfs序,top[]存当前节点所在重链的根节点
     15 void add(int u, int v) {
     16     e[++tot].net = head[u];
     17     e[tot].v = v;
     18     head[u] = tot;
     19 }
     20 void push(int x, int l) {//更新懒标 
     21     v[ls] += v[x];
     22     v[rs] += v[x];
     23     a[ls] += v[x] * (l - l / 2);//这里上下互换也可以,只是为了将这条链分成两部分。 
     24                                 //又考虑到长度不可能全是偶数,所以一个上取整,另一个下取整。 
     25     a[rs] += v[x] * (l / 2);
     26     a[ls] %= p;
     27     a[rs] %= p;
     28     v[x] = 0;
     29 }
     30 void build(int x, int l, int r) {
     31     if (l == r) {
     32         a[x] = wt[l];//wt[]数组存的是dfs遍历后的点 
     33         a[x] %= p;
     34         return;
     35     }
     36     int mid = (l + r) / 2;
     37     build(ls, l, mid);
     38     build(rs, mid + 1, r);
     39     a[x] = (a[ls] + a[rs]) % p;
     40 }
     41 int query(int x, int l, int r, int L, int R) {//线段树维护和 
     42     int res = 0;
     43     if (L <= l && r <= R) {
     44         res += a[x];
     45         res %= p;
     46         return res;
     47     }
     48     if (v[x]) {
     49         push(x, r - l + 1);
     50     }
     51     int mid = (l + r) / 2;
     52     if (L <= mid) {
     53         res += query(ls, l, mid, L, R);
     54     }
     55     if (R > mid) {
     56         res += query(rs, mid + 1, r, L, R);
     57     }
     58     return res % p;
     59 }
     60 void change(int x, int l, int r, int L, int R, int k) {//十分正常的修改 
     61     if (L <= l && r <= R) {
     62         v[x] += k;
     63         a[x] += k * (r - l + 1);
     64         a[x] %= p;
     65         return;
     66     }
     67     if (v[x]) {
     68         push(x, r - l + 1);
     69     }
     70     int mid = (l + r) / 2;
     71     if (L <= mid) {
     72         change(ls, l, mid, L, R, k);
     73     }
     74     if(R > mid) {
     75         change(rs, mid + 1, r, L, R, k);
     76     }
     77     a[x] = (a[ls] + a[rs]) % p;
     78 }
     79 void Qchange(int x, int y, int k) {
     80     k %= p;
     81     while (top[x] != top[y]) {//当这两个点不在同一条重链上 
     82         if (d[top[x]] < d[top[y]]) {//调整深度,让x变成更深的那一个
     83             swap(x, y);
     84         }
     85         change(1, 1, n, id[top[x]], id[x], k);//修改 
     86         x = fa[top[x]];//每次结束后都要跳到父亲那里 
     87     }
     88     if (d[x] > d[y]) {//当两节点在一条重链上但不是同一节点时
     89                       //调整深度 
     90         swap(x, y);
     91     }
     92     change(1, 1, n, id[x], id[y], k);//在最后的距离上进行修改 
     93 }
     94 int Qquery(int x, int y) {
     95     int ans = 0;
     96     while (top[x] != top[y]) {//当这两个点不在同一条重链上 
     97         if (d[top[x]] < d[top[y]]) {//调整深度,让x变成更深的那一个
     98             swap(x, y);
     99         }
    100         ans += query(1, 1, n, id[top[x]], id[x]);//累加答案 
    101         ans %= p;
    102         x = fa[top[x]];//每次结束后都要跳到父亲那里 
    103     }
    104     if (d[x] > d[y]) {
    105         swap(x, y);
    106     }
    107     ans += query(1, 1, n, id[x], id[y]);
    108     return ans % p;
    109 }
    110 void Schange(int x, int k) {
    111     change(1, 1, n, id[x], id[x] + siz[x] - 1, k);
    112     //因为要修改以x为根节点的子树,所以边界就是 id[x] 到 id[x] + siz[x] - 1
    113 }
    114 int Squery(int x) {
    115     return query(1, 1, n, id[x], id[x] + siz[x] - 1);//同上 
    116 }
    117 void dfs1(int x, int f, int deep) {//核心函数1
    118     d[x] = deep;//深度 
    119     fa[x] = f;//父亲节点
    120     siz[x] = 1;//当前节点大小为1 
    121     int maxson = -1;
    122     for (int i = head[x]; i; i = e[i].net) {//枚举所有出边 
    123         int to = e[i].v;
    124         if (to == f) {//肯定不能回到自己父亲吧~ 
    125             continue;
    126         }
    127         dfs1(to, x, deep + 1);//继续dfs 
    128         siz[x] += siz[to];//大小累加 
    129         if (siz[to] > maxson) {//跟新重儿子 
    130             son[x] = to;
    131             maxson = siz[to];
    132         }
    133     }
    134 }
    135 void dfs2(int x, int topf) {//核心函数2 
    136     id[x] = ++cnt;//按照重链dfs来储存新的dfs序 
    137     wt[cnt] = w[x];//储存下来 
    138     top[x] = topf;//因为在同一重链上,直接赋值 
    139     if (!son[x]) {//如果当前节点没有重儿子就停止 
    140         return;
    141     }
    142     dfs2(son[x], topf);//优先重儿子进行深搜 
    143     for (int i = head[x]; i; i = e[i].net) {
    144         int to = e[i].v;
    145         if (to == fa[x] || to == son[x]) {//如果是自己的父亲或者是重儿子就跳过 
    146             continue;
    147         }
    148         dfs2(to, to);//因为轻儿子不在重链上,所以它的top就是自己 
    149     }
    150 }
    151 int main() {
    152     scanf("%d%d%d%d", &n, &m, &r, &p);
    153     for (int i = 1; i <= n; i++) {
    154         scanf("%d", &w[i]);
    155     }
    156     for (int i = 1; i < n; i++) {
    157         int u, v;
    158         scanf("%d%d", &u, &v);
    159         add(u, v);
    160         add(v, u);
    161     }
    162     dfs1(r, 0, 1);
    163     dfs2(r, r);
    164     build(1, 1, n);
    165     for (int i = 1; i <= m; i++) {
    166         int o, x, y, z;
    167         scanf("%d", &o);
    168         if (o == 1) {
    169             scanf("%d%d%d", &x, &y, &z);
    170             Qchange(x, y, z);
    171         } else if (o == 2) {
    172             scanf("%d%d", &x, &y);
    173             printf("%d
    ", Qquery(x, y));
    174         } else if (o == 3) {
    175             scanf("%d%d", &x, &z);
    176             Schange(x, z);
    177         } else {
    178             scanf("%d", &x);
    179             printf("%d
    ", Squery(x));
    180         }
    181     }
    182     return 0;
    183 }
    View Code
  • 相关阅读:
    win10系统激活 快捷方式
    echarts 图表自适应外部盒子大小
    JS开发常用工具函数 总结
    课程学习总结报告
    结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程
    基于mykernel 2.0编写一个操作系统内核
    框架复习_SpringMvc
    框架复习_Mybatis
    框架复习_Spring
    IDEA调试
  • 原文地址:https://www.cnblogs.com/Sundial/p/11830434.html
Copyright © 2011-2022 走看看