树链剖分模板

题目连接:https://www.luogu.com.cn/problem/P3384

题目描述

如题,已知一棵包含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 #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 const int maxn=200000+10;
 12 int n,m,r,mod;
 13 //见题意
 14 int w[maxn],wt[maxn];
 15 //链式前向星数组,w[]、wt[]初始点权数组
 16 int son[maxn],id[maxn],fa[maxn],cnt,dep[maxn],siz[maxn],top[maxn];
 17 //son[]重儿子编号,id[]新编号,fa[]父亲节点,cnt dfs_clock/dfs序,dep[]深度,siz[]子树大小,top[]当前链顶端节点
 18 //查询答案
 19 struct node
 20 {
 21     int v,nxt;
 22 }G[maxn<<2]; int head[maxn]; int num;
 23 struct tre
 24 {
 25     int l,r,lazy,sum;
 26 }tree[maxn<<2];
 27 void add(int u,int v)
 28 {
 29     G[++num].v=v;G[num].nxt=head[u];head[u]=num;
 30     G[++num].v=u;G[num].nxt=head[v];head[v]=num;
 31 }
 32 void pushdown(int rt,int lenn){
 33     tree[rt<<1].lazy+=tree[rt].lazy;
 34     tree[rt<<1|1].lazy+=tree[rt].lazy;
 35     tree[rt<<1].sum+=tree[rt].lazy*(lenn-(lenn>>1));
 36     tree[rt<<1|1].sum+=tree[rt].lazy*(lenn>>1);
 37     tree[rt<<1].sum%=mod;
 38     tree[rt<<1|1].sum%=mod;
 39     tree[rt].lazy=0;
 40 }
 41 
 42 void build(int l,int r,int root){
 43     tree[root].l=l;tree[root].r=r;
 44     tree[root].sum=tree[root].lazy=0;
 45     if(l==r){
 46         tree[root].sum=wt[l];
 47         if(tree[root].sum>mod)tree[root].sum%=mod;
 48         return;
 49     }
 50     int mid=l+r>>1;
 51     build(l,mid,root<<1);
 52     build(mid+1,r,root<<1|1);
 53     tree[root].sum=(tree[root<<1].sum+tree[root<<1|1].sum)%mod;
 54 }
 55 
 56 int query(int l,int r,int root){
 57     int L=tree[root].l;int R=tree[root].r;
 58     if(l<=L&&R<=r){
 59         return tree[root].sum%mod;
 60     }
 61     if(tree[root].lazy)pushdown(root,R-L+1);
 62     int mid=L+R>>1;
 63     int ans=0;
 64     if(l<=mid) ans+=query(l,r,root<<1),ans%=mod;
 65     if(r>mid)  ans+=query(l,r,root<<1|1),ans%=mod;
 66     return ans;
 67 }
 68 void update(int l,int r,int val,int root){
 69     int L=tree[root].l;int R=tree[root].r;
 70     if(l<=L&&R<=r){
 71         tree[root].lazy+=val;
 72         tree[root].sum+=val*(R-L+1);
 73     }
 74     else{
 75         if(tree[root].lazy)pushdown(root,R-L+1);
 76         int mid=L+R>>1;
 77         if(l<=mid)update(l,r,val,root<<1);
 78         if(r>mid) update(l,r,val,root<<1|1);
 79         tree[root].sum=(tree[root<<1].sum+tree[root<<1|1].sum)%mod;
 80     }
 81 }
 82 int qRange(int x,int y){
 83     int ans=0;
 84     while(top[x]!=top[y]){//当两个点不在同一条链上
 85         if(dep[top[x]]<dep[top[y]])swap(x,y);//把x点改为所在链顶端的深度更深的那个点
 86         ans+=query(id[top[x]],id[x],1);//ans加上x点到x所在链顶端 这一段区间的点权和
 87         ans%=mod;//按题意取模
 88         x=fa[top[x]];//把x跳到x所在链顶端的那个点的上面一个点
 89     }
 90     //直到两个点处于一条链上
 91     if(dep[x]>dep[y])swap(x,y);//把x点深度更深的那个点
 92     ans+=query(id[x],id[y],1);//这时再加上此时两个点的区间和即可
 93     return ans%mod;
 94 }
 95 
 96 void updRange(int x,int y,int k){//同上
 97     k%=mod;
 98     while(top[x]!=top[y]){
 99         if(dep[top[x]]<dep[top[y]])swap(x,y);
100         update(id[top[x]],id[x],k,1);
101         x=fa[top[x]];
102     }
103     if(dep[x]>dep[y])swap(x,y);
104     update(id[x],id[y],k,1);
105 }
106 
107 void dfs1(int u,int f,int deep){//x当前节点,f父亲,deep深度
108     dep[u]=deep;//标记每个点的深度
109     fa[u]=f;//标记每个点的父亲
110     siz[u]=1;//标记每个非叶子节点的子树大小
111     int maxson=-1;//记录重儿子的儿子数
112     for(int i=head[u];i!=-1;i=G[i].nxt){
113         int v=G[i].v;
114         if(v==f)continue;//若为父亲则continue
115         dfs1(v,u,deep+1);//dfs其儿子
116         siz[u]+=siz[v];//把它的儿子数加到它身上
117         if(siz[v]>maxson)son[u]=v,maxson=siz[v];//标记每个非叶子节点的重儿子编号
118     }
119 }
120 
121 void dfs2(int u,int topf){//x当前节点,topf当前链的最顶端的节点
122     id[u]=++cnt;//标记每个点的新编号
123     wt[cnt]=w[u];//把每个点的初始值赋到新编号上来
124     top[u]=topf;//这个点所在链的顶端
125     if(!son[u])return;//如果没有儿子则返回
126     dfs2(son[u],topf);//按先处理重儿子,再处理轻儿子的顺序递归处理
127     for(int i=head[u];i!=-1;i=G[i].nxt){
128         int v=G[i].v;
129         if(v==fa[u]||v==son[u])continue;
130         dfs2(v,v);//对于每一个轻儿子都有一条从它自己开始的链
131     }
132 }
133 void init()
134 {
135     memset(head,-1,sizeof(head));
136     num=-1;
137 }
138 int main(){
139     init();
140     scanf("%d%d%d%d",&n,&m,&r,&mod);
141     for(int i=1;i<=n;i++) scanf("%d",&w[i]);
142     for(int i=1;i<n;i++){
143         int u,v;
144         scanf("%d%d",&u,&v);
145         add(u,v);
146     }
147     dfs1(r,0,1);
148     dfs2(r,r);
149     build(1,n,1);
150     while(m--){
151         int k,x,y,z;
152         scanf("%d",&k);
153         if(k==1){
154             scanf("%d%d%d",&x,&y,&z);
155             updRange(x,y,z);
156         }
157         else if(k==2){
158             scanf("%d%d",&x,&y);
159             printf("%d\n",qRange(x,y));
160         }
161         else if(k==3){
162             scanf("%d%d",&x,&y);
163             update(id[x],id[x]+siz[x]-1,y,1);
164         }
165         else{
166             scanf("%d",&x);
167             printf("%d\n",query(id[x],id[x]+siz[x]-1,1));
168         }
169     }
170     return 0;
171 }

 

posted @ 2020-02-12 22:19  古比  阅读(115)  评论(0编辑  收藏  举报