• 树链剖分入门(附代码详细注释)


     推荐一个大佬的视频:here

    模板题:P3384 【模板】轻重链剖分

     

     模板:

      1 #include <bits/stdc++.h>
      2 typedef long long ll;
      3 using namespace std;
      4 const int maxn = 1e5+5;
      5 
      6 //dfs序可以让一些链上的节点在一个连续的区间
      7 //树链剖分则是强制地让重链上的节点在一个连续的区间
      8 //重链剖分不改变dfs序的性质:一棵子树所在的位置处于一个连续区间中
      9 
     10 
     11 struct node{
     12     int to,nxt;
     13 }e[maxn<<1];
     14 
     15 struct Tree{
     16     int l,r,f,val;//区间左边界,右边界,lazy标记,区间和
     17 }tree[maxn<<2];
     18 
     19 int tot,head[maxn];
     20 
     21 int n,m,r,mod;
     22 int v[maxn];
     23 
     24 void init(){
     25     memset(head,-1,sizeof(head));
     26     tot=0;
     27 }
     28 
     29 void addedge(int u,int v){
     30     e[tot].to=v; e[tot].nxt=head[u]; head[u]=tot++;
     31 }
     32 
     33 int fa[maxn],dep[maxn],siz[maxn],son[maxn];
     34 void dfs1(int u,int f){
     35     fa[u] = f;
     36     dep[u] = dep[f]+1;
     37     siz[u] = 1;
     38     int maxsize = -1;   //判断重儿子的变量
     39     for(int i=head[u];~i;i=e[i].nxt){
     40         int v=e[i].to;
     41         if( v==f ) continue;
     42         dfs1(v,u);
     43         siz[u] += siz[v];
     44         if( siz[v]>maxsize ){
     45             maxsize = siz[v];
     46             son[u] = v; //son存重儿子
     47         }
     48     }
     49 }
     50 
     51 //计数器, dfs序时间戳, 重链的头, 节点权值的dfs序
     52 int tim, dfn[maxn],top[maxn],w[maxn];
     53 void dfs2(int u,int t){
     54     dfn[u] = ++tim;
     55     top[u] = t;
     56     w[tim] = v[u];
     57     if( !son[u] ) return ;//这个节点没有重儿子,就是叶子结点
     58     dfs2(son[u],t); //先往重儿子走,
     59     for(int i=head[u];~i;i=e[i].nxt){   //跑轻儿子
     60         int v = e[i].to;
     61         if( v==fa[u] || v==son[u] ) continue;
     62         dfs2(v,v);  //新开始一条重链,头是自己本身
     63     }
     64 }
     65 
     66 void pushup(int rt){//裸线段树向上维护区间和
     67     tree[rt].val = (tree[rt<<1].val + tree[rt<<1|1].val)%mod;
     68 }
     69 
     70 void pushdown(int rt){//根据lazy标记下放
     71     tree[rt<<1].f += tree[rt].f;
     72     tree[rt<<1|1].f += tree[rt].f;
     73     tree[rt<<1].val += (tree[rt<<1].r-tree[rt<<1].l+1)*tree[rt].f%mod;
     74     tree[rt<<1|1].val += (tree[rt<<1|1].r-tree[rt<<1|1].l+1) * tree[rt].f %mod;
     75     tree[rt].f = 0;
     76 }
     77 
     78 void build(int l,int r,int rt=1){//裸线段树建树
     79     tree[rt].l=l, tree[rt].r=r;
     80     if( l==r ){
     81         tree[rt].val = w[l]%mod;
     82         return ;
     83     }
     84     int mid = (l+r)>>1;
     85     build(l,mid,rt<<1);
     86     build(mid+1,r,rt<<1|1);
     87     pushup(rt);
     88 }
     89 
     90 void modify(int x, int y, int z, int rt=1){//线段树修改
     91     int l=tree[rt].l, r=tree[rt].r;
     92     if( x<=l && y>=r ){
     93         tree[rt].f += z;
     94         tree[rt].val += (r-l+1)*z;
     95         tree[rt].val %= mod;
     96         return ;
     97     }
     98     if( tree[rt].f ){
     99         pushdown(rt);
    100     }
    101     int mid = (l+r)>>1;
    102     if( x<=mid ) modify(x,y,z,rt<<1);
    103     if( y>mid ) modify(x,y,z,rt<<1|1);
    104     pushup(rt);
    105 }
    106 
    107 int query(int x,int y,int rt=1){   //线段树区间查询
    108     int l=tree[rt].l, r=tree[rt].r;
    109     if( x<=l && y>=r ){
    110         return tree[rt].val;
    111     }
    112     if( tree[rt].f ) pushdown(rt);
    113     int sum=0, mid=(l+r)>>1;
    114     if( x<=mid ) sum += query(x,y,rt<<1);
    115     if( y>mid ) sum += query(x,y,rt<<1|1);
    116     return sum%mod;
    117 }
    118 
    119 //重要引理:除根节点以外的任何一个结点的父亲结点都一定在一条重链上
    120 //证明:因为父亲结点存在儿子,所以一定存在重儿子,所以一定在一条重链上
    121 void mchain(int x,int y,int z){
    122     z %= mod;
    123     while( top[x]!=top[y] ){
    124         if( dep[top[x]]<dep[top[y]] ){//注意这里是top[x],top[y],不是直接的x,y
    125             swap(x,y);  //有点类似LCA那里
    126         }
    127         modify(dfn[top[x]], dfn[x], z); //区间修改
    128         x = fa[top[x]]; //到这条链的头的父亲节点(看引理)
    129     }
    130 
    131     if( dep[x]>dep[y] ){
    132         swap(x,y);
    133     }
    134     modify(dfn[x], dfn[y], z);  //现在在一条链上了,所以是x,y,周时候就不要调到链的头上了,把x,y跳到一起就好了
    135 }
    136 
    137 int qchain(int x,int y){
    138     int ret=0;
    139     while( top[x]!=top[y] ){
    140         if( dep[top[x]]<dep[top[y]] ) swap(x,y);
    141         ret += query(dfn[top[x]],dfn[x]);
    142         x = fa[top[x]];
    143     }
    144     if( dep[x]>dep[y] ) swap(x,y);
    145     ret += query(dfn[x],dfn[y]);
    146     return ret % mod;
    147 }
    148 
    149 void mson(int x,int z){
    150     modify(dfn[x], dfn[x]+siz[x]-1,z);//依据dfs序的性质
    151 }
    152 
    153 int qson(int x){
    154     return query(dfn[x], dfn[x]+siz[x]-1);//依据dfs序的性质
    155 }
    156 
    157 int main()
    158 {
    159     init();
    160     scanf("%d%d%d%d",&n,&m,&r,&mod);
    161     for(int i=1;i<=n;i++) scanf("%d",&v[i]);
    162     for(int i=1;i<n;i++){
    163         int u,v; scanf("%d%d",&u,&v);
    164         addedge(u,v); addedge(v,u);
    165     }
    166     dfs1(r,r);
    167     dfs2(r,r);
    168     //注意转换思维当处理完dfs序与重链剖分后,就变换成数组来做题
    169     build(1,n);//根据dfs序【就是一个数组】建立裸的线段树
    170     while( m-- ){
    171         int opt,x,y,z;
    172         scanf("%d",&opt);
    173         switch(opt){
    174         case 1:
    175             scanf("%d%d%d",&x,&y,&z);   //1 x y z 表示将树从 x 到 y 结点最短路径上所有节点的值都加上 z
    176             mchain(x,y,z);
    177             break;
    178         case 2:
    179             scanf("%d%d",&x,&y);    //2 x y 表示求树从 x 到 y 结点最短路径上所有节点的值之和
    180             printf("%d
    ",qchain(x,y));
    181             break;
    182         case 3:
    183             scanf("%d%d",&x,&z);    //3 x z 表示将以 x 为根节点的子树内所有节点值都加上 z
    184             mson(x,z);
    185             break;
    186         case 4:
    187             scanf("%d",&x);  //4 x 表示求以 x 为根节点的子树内所有节点值之和
    188             printf("%d
    ",qson(x));
    189             break;
    190         }
    191     }
    192     return 0;
    193 }
  • 相关阅读:
    oracle数据库连接不上
    Oracle的regexp_instr函数简单用法
    Oracle中dbms_random.string 的用法
    oracle 简单输出语句与赋值
    oracle中sequence(自增序号)的用法
    oracle 函数的返回值与out参数
    SQL%ROWCOUNT作用
    100多个基础常用JS函数和语法集合大全
    题解【AcWing272】最长公共上升子序列
    题解【POJ1062】昂贵的聘礼
  • 原文地址:https://www.cnblogs.com/wsy107316/p/13791001.html
Copyright © 2020-2023  润新知