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

    摘自博客:https://blog.csdn.net/VictoryCzt/article/details/85268376

    博主讲得十分的清楚

    以下是带有注释的代码:

      1 /*
      2 这是一份自用的树链剖分模板,
      3 自己加的注释,便于更好的理解
      4 该模板支持的操作:
      5 1.换根
      6 2.一条链上点权加
      7 3.一个子树内点权加
      8 4.询问一条链上点权的和
      9 5.询问一个子树内的点权和
     10 6.求LCA
     11 */
     12 #include<bits/stdc++.h>
     13 #define numm ch-48
     14 #define pd putchar(' ')
     15 #define pn putchar('
    ')
     16 #define pb push_back
     17 #define debug(args...) cout<<#args<<"->"<<args<<endl
     18 #define bug cout<<"************"
     19 using namespace std;
     20 template <typename T>
     21 void read(T &res) {
     22     bool flag=false;char ch;
     23     while(!isdigit(ch=getchar())) (ch=='-')&&(flag=true);
     24     for(res=numm;isdigit(ch=getchar());res=(res<<1)+(res<<3)+numm);
     25     flag&&(res=-res);
     26 }
     27 template <typename T>
     28 void write(T x) {
     29     if(x<0) putchar('-'),x=-x;
     30     if(x>9) write(x/10);
     31     putchar(x%10+'0');
     32 }
     33 typedef long long ll;
     34 const int inf=0x3f3f3f3f;
     35 const int maxn=1e5+10;
     36 /*建图部分*/
     37 struct node {///链式前向星
     38     int v,net;
     39     node(){}
     40     node(int v,int net):v(v),net(net){}
     41 }e[maxn<<1];///树有双向边,边数*2
     42 int head[maxn],cnt,n;
     43 void init() {
     44     cnt=0;
     45     for(int i=1;i<=n;i++)
     46         head[i]=-1;
     47 }
     48 void addedge(int u,int v) {///建双向边
     49     e[cnt]=node(v,head[u]);
     50     head[u]=cnt++;
     51     e[cnt]=node(u,head[v]);
     52     head[v]=cnt++;
     53 }
     54 int id[maxn],rk[maxn],fa[maxn],top[maxn],dep[maxn],son[maxn],sze[maxn],val[maxn],root=1;
     55 //id[u]:点u的dfs序,一条重链上的点标号连续
     56 //sze[u]:点u的子树结点个数,包含点u
     57 //son[u]:点u的重儿子的id
     58 //fa[u]:点u的父亲,根结点1的父亲为0
     59 //top[u]:点u所在重链的头结点,轻链则为本身,则top[u]=u;
     60 //val[u]:点u的初始权值
     61 //rk[id[u]]:id[u]对应的u结点
     62 //dep[u]:dfs时,u对应的深度
     63 int minp[maxn<<2];
     64 ll sum[maxn<<2],lazy[maxn<<2];
     65 /*线段树部分*/
     66 //sum[k]:点k所在区间的树结点总和
     67 //lazy[k]:点k所在区间的树结点总和的懒标记
     68 //minp[k]:点k所在区间内的最小深度的点u的下标
     69 int get_minp(int a,int b) { ///返回a,b结点中深度最小的结点
     70     if(a==inf) return b;
     71     if(b==inf) return a;
     72     if(dep[a]<dep[b]) return a;
     73     return b;
     74 }
     75 void pushup(int k) {
     76     sum[k]=sum[k<<1]+sum[k<<1|1];
     77     minp[k]=get_minp(minp[k<<1],minp[k<<1|1]);
     78 }
     79 void pushdown(int k,int l,int r,int mid) {
     80     if(!lazy[k]) return ;
     81     sum[k<<1]+=(mid-l+1)*lazy[k];
     82     sum[k<<1|1]+=(r-mid)*lazy[k];
     83     lazy[k<<1]+=lazy[k];
     84     lazy[k<<1|1]+=lazy[k];
     85     lazy[k]=0;
     86 }
     87 void build(int k,int l,int r) {///建树
     88     if(l==r) {
     89         sum[k]=val[rk[l]];
     90         minp[k]=rk[l];
     91         return ;
     92     }
     93     int mid=l+r>>1;
     94     build(k<<1,l,mid);
     95     build(k<<1|1,mid+1,r);
     96     pushup(k);
     97 }
     98 void update(int k,int l,int r,int ql,int qr,ll w) {///区间修改
     99     if(ql<=l&&r<=qr) {
    100         sum[k]+=(r-l+1)*w;
    101         lazy[k]+=w;
    102         return ;
    103     }
    104     int mid=l+r>>1;
    105     pushdown(k,l,r,mid);///attention!!!
    106     if(ql<=mid) update(k<<1,l,mid,ql,qr,w);
    107     if(mid<qr) update(k<<1|1,mid+1,r,ql,qr,w);
    108     pushup(k);
    109 }
    110 int query_pos(int k,int l,int r,int ql,int qr) {///区间查询最小深度的结点
    111     if(ql>qr) return inf;
    112     if(ql<=l&&r<=qr) return minp[k];
    113     int mid=l+r>>1;
    114     if(qr<=mid) return query_pos(k<<1,l,mid,ql,qr);
    115     else if(ql>mid) return query_pos(k<<1|1,mid+1,r,ql,qr);
    116     return get_minp(query_pos(k<<1,l,mid,ql,qr),query_pos(k<<1|1,mid+1,r,ql,qr));
    117 }
    118 ll query_sum(int k,int l,int r,int ql,int qr) {///区间求和
    119     if(ql<=l&&r<=qr) return sum[k];
    120     int mid=l+r>>1;
    121     pushdown(k,l,r,mid);
    122     if(qr<=mid) return query_sum(k<<1,l,mid,ql,qr);
    123     else if(ql>mid) return query_sum(k<<1|1,mid+1,r,ql,qr);
    124     else return query_sum(k<<1,l,mid,ql,qr)+query_sum(k<<1|1,mid+1,r,ql,qr);
    125 }
    126 /*树剖部分*/
    127 void dfs1(int u) {  ///得到每个点的深度,父亲,,子树大小和重儿子
    128     sze[u]=1;   ///把自己算进去
    129     for(int i=head[u];~i;i=e[i].net) {
    130         int v=e[i].v;
    131         if(v==fa[u]) continue;
    132         fa[v]=u;
    133         dep[v]=dep[u]+1;
    134         dfs1(v);
    135         sze[u]+=sze[v]; ///回溯累加
    136         if(!son[u]||sze[son[u]]<sze[v])///更新重儿子
    137             son[u]=v;
    138     }
    139 }
    140 void dfs2(int u,int root) {
    141     top[u]=root;
    142     id[u]=++cnt;
    143     rk[cnt]=u;
    144     if(!son[u]) return ;
    145     dfs2(son[u],root);
    146     ///先dfs重儿子,使得每条重链上的结点标号连续
    147     for(int i=head[u];~i;i=e[i].net) {
    148         int v=e[i].v;
    149         if(v==son[u]||v==fa[u]) continue;
    150         dfs2(v,v);///轻链的top则为本身
    151     }
    152 }
    153 int lca(int a,int b) {  ///求公共祖先
    154     while(top[a]!=top[b]) {
    155         if(dep[top[a]]<dep[top[b]]) swap(a,b);
    156         a=fa[top[a]];
    157     }
    158     if(dep[a]>dep[b]) swap(a,b);
    159     return a;
    160 }
    161 int Upto(int a,int b) { ///返回a到b上深度最小的点的下标
    162     int ans=inf;
    163     while(top[a]!=top[b]) {
    164         if(dep[top[a]]<dep[top[b]]) swap(a,b);
    165         ///不能包含u点,要分类讨论
    166         if(top[a]!=b) ans=get_minp(ans,query_pos(1,1,n,id[top[a]],id[a]));
    167         else ans=get_minp(ans,query_pos(1,1,n,id[top[a]]+1,id[a]));
    168         a=fa[top[a]];
    169     }
    170     if(dep[a]>dep[b]) swap(a,b);
    171     
    172     ans=get_minp(ans,query_pos(1,1,n,id[a]+1,id[b]));///不能包含u点,所以+1
    173     return ans;
    174 }
    175 int check(int u) {  ///判r与root的关系
    176     if(u==root) return 0;///此时操作范围就是整个树,将整个树加或者求和即可。
    177     int L=lca(u,root);
    178     if(L==u) return Upto(u,root);/*当r在u的子树内时,操作范围就为
    179     整个树减去u到r路径上深度最小的点(不包含u点)的原来的子树*/
    180     else return -1;///当r不在u的原来的子树里面时,操作范围就是原来的子树。
    181 }
    182 void add_chain(int a,int b,ll w) { ///一条链上点权加
    183     while(top[a]!=top[b]) {
    184         if(dep[top[a]]<dep[top[b]]) swap(a,b);///找到深度最大的点
    185         update(1,1,n,id[top[a]],id[a],w);
    186         a=fa[top[a]];   ///一次跳一个
    187     }
    188     if(dep[a]>dep[b]) swap(a,b);
    189     update(1,1,n,id[a],id[b],w);
    190 }
    191 ll ask_chain(int a,int b) { ///询问一条链上点权的和
    192     ll ans=0;
    193     while(top[a]!=top[b]) {
    194         if(dep[top[a]]<dep[top[b]]) swap(a,b);
    195         ans+=query_sum(1,1,n,id[top[a]],id[a]);
    196         a=fa[top[a]];
    197     }
    198     if(dep[a]>dep[b]) swap(a,b);
    199     ans+=query_sum(1,1,n,id[a],id[b]);
    200     return ans;
    201 }
    202 void add_tree(int u,ll w) { ///一个子树内点权加
    203     int type=check(u);  ///判断u与root的情况
    204     if(!type) update(1,1,n,1,n,w);
    205     else if(type>0) {
    206         update(1,1,n,1,n,w);
    207         update(1,1,n,id[type],id[type]+sze[type]-1,-w);
    208     }
    209     else update(1,1,n,id[u],id[u]+sze[u]-1,w);
    210 }
    211 ll ask_tree(int u) {    ///询问u的子树内的点权和
    212     int type=check(u); ///判断u与root的情况
    213     if(!type) return query_sum(1,1,n,1,n);
    214     else if(type>0){
    215         ll ans=query_sum(1,1,n,id[type],id[type]+sze[type]-1);
    216         return query_sum(1,1,n,1,n)-ans;
    217     }
    218     else return query_sum(1,1,n,id[u],id[u]+sze[u]-1);
    219 }
    220 int main()
    221 {
    222     int a,b;
    223     ll w;
    224     read(n);
    225     init();
    226     for(int i=1;i<=n;i++) ///得到每个点的初始权值
    227         read(val[i]);
    228     for(int i=2;i<=n;i++) { ///得到每个点的父亲
    229         read(a);
    230         addedge(a,i);   ///建边
    231     }
    232     dfs1(root);
    233     cnt=0;  ///attention!!!
    234     dfs2(1,1);
    235     build(1,1,n);
    236     int m;
    237     read(m);
    238     while(m--) {
    239         int oper;
    240         read(oper);
    241         if(oper==1) {   ///换根
    242             read(a);
    243             root=a;
    244         }
    245         else if(oper==2) {  ///一条链上点权加
    246             read(a),read(b),read(w);
    247             add_chain(a,b,w);
    248         }
    249         else if(oper==3) {   ///一个子树内点权加
    250             read(a),read(w);
    251             add_tree(a,w);
    252         }
    253         else if(oper==4) {  ///询问一条链上点权的和
    254             read(a),read(b);
    255             write(ask_chain(a,b));pn;
    256         }
    257         else if(oper==5) { ///询问一个子树内的点权和
    258             read(a);
    259             write(ask_tree(a));pn;
    260         }
    261     }
    262     return 0;
    263 }
    View Code
  • 相关阅读:
    007-搭建框架-开发AOP框架
    007-安装百度云,搜狗输入法,播放器
    006-重装yum
    005-快捷键,host,查看版本
    004-ibus输入法,快捷键,浏览器
    003-centos搭建idea开发java
    002-命令行模式安装图形界面
    006-网站统计中的数据收集原理及实现
    007-sql整体概述
    017-Hadoop Hive sql语法详解7-去重排序、数据倾斜
  • 原文地址:https://www.cnblogs.com/wuliking/p/11614770.html
Copyright © 2011-2022 走看看