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

    题目描述

    如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:

    操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z

    操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和

    操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z

    操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和

    输入格式

    第一行包含4个正整数N、M、R、P,分别表示树的结点个数、操作个数、根节点序号和取模数(即所有的输出结果均对此取模)。

    接下来一行包含N个非负整数,分别依次表示各个节点上初始的数值。

    接下来N-1行每行包含两个整数x、y,表示点x和点y之间连有一条边(保证无环且连通)

    接下来M行每行包含若干个正整数,每行表示一个操作,格式如下:

    操作1: 1 x y z

    操作2: 2 x y

    操作3: 3 x z

    操作4: 4 x

    输出格式

    输出包含若干行,分别依次表示每个操作2或操作4所得的结果(对P取模)

    输入输出样例

    输入 #1

    5 5 2 24
    7 3 7 8 0 
    1 2
    1 5
    3 1
    4 1
    3 4 2
    3 2 2
    4 5
    1 5 1 3
    2 1 3

    输出 #1

    2
    21

    说明/提示

    时空限制:1s,128M

    数据规模:

    对于30%的数据:N10,M10

    对于70%的数据:N10^3,M10^3

    对于100%的数据: N10^5,M10^5

    ( 其实,纯随机生成的树LCA+暴力是能过的,可是,你觉得可能是纯随机的么233 )

    样例说明:

    树的结构如下:

    各个操作如下:

    故输出应依次为2、21(重要的事情说三遍:记得取模)

     

    分析:

    一道模板题,除了码量较大其他好像也没什么要强调的emmm

    CODE(话说我很久前写的怎么被删了233333这里只好用一下大佬的):

      1 #include<algorithm>
      2 #include<iostream>
      3 #include<cstdlib>
      4 #include<cstring>
      5 #include<cstdio>
      6 #define Rint register int
      7 #define mem(a,b) memset(a,(b),sizeof(a))
      8 #define Temp template<typename T>
      9 using namespace std;
     10 typedef long long LL;
     11 Temp inline void read(T &x){
     12     x=0;T w=1,ch=getchar();
     13     while(!isdigit(ch)&&ch!='-')ch=getchar();
     14     if(ch=='-')w=-1,ch=getchar();
     15     while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();
     16     x=x*w;
     17 }
     18 
     19 #define mid ((l+r)>>1)
     20 #define lson rt<<1,l,mid
     21 #define rson rt<<1|1,mid+1,r
     22 #define len (r-l+1)
     23 
     24 const int maxn=200000+10;
     25 int n,m,r,mod;
     26 //见题意 
     27 int e,beg[maxn],nex[maxn],to[maxn],w[maxn],wt[maxn];
     28 //链式前向星数组,w[]、wt[]初始点权数组 
     29 int a[maxn<<2],laz[maxn<<2];
     30 //线段树数组、lazy操作 
     31 int son[maxn],id[maxn],fa[maxn],cnt,dep[maxn],siz[maxn],top[maxn]; 
     32 //son[]重儿子编号,id[]新编号,fa[]父亲节点,cnt dfs_clock/dfs序,dep[]深度,siz[]子树大小,top[]当前链顶端节点 
     33 int res=0;
     34 //查询答案 
     35 
     36 inline void add(int x,int y){//链式前向星加边 
     37     to[++e]=y;
     38     nex[e]=beg[x];
     39     beg[x]=e;
     40 }
     41 //-------------------------------------- 以下为线段树 
     42 inline void pushdown(int rt,int lenn){
     43     laz[rt<<1]+=laz[rt];
     44     laz[rt<<1|1]+=laz[rt];
     45     a[rt<<1]+=laz[rt]*(lenn-(lenn>>1));
     46     a[rt<<1|1]+=laz[rt]*(lenn>>1);
     47     a[rt<<1]%=mod;
     48     a[rt<<1|1]%=mod;
     49     laz[rt]=0;
     50 }
     51 
     52 inline void build(int rt,int l,int r){
     53     if(l==r){
     54         a[rt]=wt[l];
     55         if(a[rt]>mod)a[rt]%=mod;
     56         return;
     57     }
     58     build(lson);
     59     build(rson);
     60     a[rt]=(a[rt<<1]+a[rt<<1|1])%mod;
     61 }
     62 
     63 inline void query(int rt,int l,int r,int L,int R){
     64     if(L<=l&&r<=R){res+=a[rt];res%=mod;return;}
     65     else{
     66         if(laz[rt])pushdown(rt,len);
     67         if(L<=mid)query(lson,L,R);
     68         if(R>mid)query(rson,L,R);
     69     }
     70 }
     71 
     72 inline void update(int rt,int l,int r,int L,int R,int k){
     73     if(L<=l&&r<=R){
     74         laz[rt]+=k;
     75         a[rt]+=k*len;
     76     }
     77     else{
     78         if(laz[rt])pushdown(rt,len);
     79         if(L<=mid)update(lson,L,R,k);
     80         if(R>mid)update(rson,L,R,k);
     81         a[rt]=(a[rt<<1]+a[rt<<1|1])%mod;
     82     }
     83 }
     84 //---------------------------------以上为线段树 
     85 inline int qRange(int x,int y){
     86     int ans=0;
     87     while(top[x]!=top[y]){//当两个点不在同一条链上 
     88         if(dep[top[x]]<dep[top[y]])swap(x,y);//把x点改为所在链顶端的深度更深的那个点
     89         res=0;
     90         query(1,1,n,id[top[x]],id[x]);//ans加上x点到x所在链顶端 这一段区间的点权和
     91         ans+=res;
     92         ans%=mod;//按题意取模 
     93         x=fa[top[x]];//把x跳到x所在链顶端的那个点的上面一个点
     94     }
     95     //直到两个点处于一条链上
     96     if(dep[x]>dep[y])swap(x,y);//把x点深度更深的那个点
     97     res=0;
     98     query(1,1,n,id[x],id[y]);//这时再加上此时两个点的区间和即可
     99     ans+=res;
    100     return ans%mod;
    101 }
    102 
    103 inline void updRange(int x,int y,int k){//同上 
    104     k%=mod;
    105     while(top[x]!=top[y]){
    106         if(dep[top[x]]<dep[top[y]])swap(x,y);
    107         update(1,1,n,id[top[x]],id[x],k);
    108         x=fa[top[x]];
    109     }
    110     if(dep[x]>dep[y])swap(x,y);
    111     update(1,1,n,id[x],id[y],k);
    112 }
    113 
    114 inline int qSon(int x){
    115     res=0;
    116     query(1,1,n,id[x],id[x]+siz[x]-1);//子树区间右端点为id[x]+siz[x]-1 
    117     return res;
    118 }
    119 
    120 inline void updSon(int x,int k){//同上 
    121     update(1,1,n,id[x],id[x]+siz[x]-1,k);
    122 }
    123 
    124 inline void dfs1(int x,int f,int deep){//x当前节点,f父亲,deep深度 
    125     dep[x]=deep;//标记每个点的深度 
    126     fa[x]=f;//标记每个点的父亲 
    127     siz[x]=1;//标记每个非叶子节点的子树大小 
    128     int maxson=-1;//记录重儿子的儿子数 
    129     for(Rint i=beg[x];i;i=nex[i]){
    130         int y=to[i];
    131         if(y==f)continue;//若为父亲则continue 
    132         dfs1(y,x,deep+1);//dfs其儿子 
    133         siz[x]+=siz[y];//把它的儿子数加到它身上 
    134         if(siz[y]>maxson)son[x]=y,maxson=siz[y];//标记每个非叶子节点的重儿子编号 
    135     }
    136 }
    137 
    138 inline void dfs2(int x,int topf){//x当前节点,topf当前链的最顶端的节点 
    139     id[x]=++cnt;//标记每个点的新编号 
    140     wt[cnt]=w[x];//把每个点的初始值赋到新编号上来 
    141     top[x]=topf;//这个点所在链的顶端 
    142     if(!son[x])return;//如果没有儿子则返回 
    143     dfs2(son[x],topf);//按先处理重儿子,再处理轻儿子的顺序递归处理 
    144     for(Rint i=beg[x];i;i=nex[i]){
    145         int y=to[i];
    146         if(y==fa[x]||y==son[x])continue;
    147         dfs2(y,y);//对于每一个轻儿子都有一条从它自己开始的链 
    148     }
    149 }
    150 
    151 int main(){
    152     read(n);read(m);read(r);read(mod);
    153     for(Rint i=1;i<=n;i++)read(w[i]);
    154     for(Rint i=1;i<n;i++){
    155         int a,b;
    156         read(a);read(b);
    157         add(a,b);add(b,a);
    158     }
    159     dfs1(r,0,1);
    160     dfs2(r,r);
    161     build(1,1,n);
    162     while(m--){
    163         int k,x,y,z;
    164         read(k);
    165         if(k==1){
    166             read(x);read(y);read(z);
    167             updRange(x,y,z);
    168         }
    169         else if(k==2){
    170             read(x);read(y);
    171             printf("%d
    ",qRange(x,y));
    172         }
    173         else if(k==3){
    174             read(x);read(y);
    175             updSon(x,y);
    176         }
    177         else{
    178             read(x);
    179             printf("%d
    ",qSon(x));
    180         }
    181     }
    182 }
  • 相关阅读:
    煲鸡汤流程
    面向对象
    程序员英语学习思维导图
    百度通配符学习
    面向对象
    IO学习
    理解java的三大特性之继承
    重载(overload)、覆盖(override)、隐藏(hide)的区别
    2018年值得关注的10大JavaScript动画库
    小知识点总结
  • 原文地址:https://www.cnblogs.com/kanchuang/p/11243556.html
Copyright © 2011-2022 走看看