洛谷 P3384 【模板】树链剖分-树链剖分(点权)(路径节点更新、路径求和、子树节点更新、子树求和)模板-备注结合一下以前写的题目,懒得写很详细的注释

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:  复制
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%的数据: N \leq 10, M \leq 10N10,M10

对于70%的数据: N \leq {10}^3, M \leq {10}^3N103,M103

对于100%的数据: N \leq {10}^5, M \leq {10}^5N105,M105

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

样例说明:

树的结构如下:

各个操作如下:

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

题意很直接,直接模板题。

写了两天,最后发现,加边时add(v,u)的括号写成[ ]了,可真是捞啊。

写了注释。

代码:

  1 //洛谷-P3384 【模板】树链剖分-树链剖分
  2 #include<iostream>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<algorithm>
  6 #include<bitset>
  7 #include<cassert>
  8 #include<cctype>
  9 #include<cmath>
 10 #include<cstdlib>
 11 #include<ctime>
 12 #include<deque>
 13 #include<iomanip>
 14 #include<list>
 15 #include<map>
 16 #include<queue>
 17 #include<set>
 18 #include<stack>
 19 #include<vector>
 20 using namespace std;
 21 typedef long long ll;
 22 
 23 const double PI=acos(-1.0);
 24 const double eps=1e-6;
 25 //const ll mod=1e9+7;
 26 const int inf=0x3f3f3f3f;
 27 const int maxn=2e5+10;
 28 const int maxm=100+10;
 29 #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
 30 #define lson l,m,rt<<1
 31 #define rson m+1,r,rt<<1|1
 32 
 33 int sum[maxn<<2],lazy[maxn<<2];
 34 int n,m,r,mod;
 35 int head[maxn],tot;
 36 
 37 int son[maxn],tid[maxn],fa[maxn],cnt,dep[maxn],siz[maxn],top[maxn];
 38 int w[maxn],wt[maxn];
 39 
 40 struct Edge{
 41     int to,next;
 42 }edge[maxn];
 43 
 44 void add(int u,int v)//链式前向星存边
 45 {
 46     edge[tot].to=v;
 47     edge[tot].next=head[u];
 48     head[u]=tot++;
 49 }
 50 
 51 void init()//初始化
 52 {
 53     memset(head,-1,sizeof(head));
 54     tot=0;cnt=0;
 55 }
 56 
 57 //线段树部分
 58 void pushup(int rt)//上传lazy标记
 59 {
 60     sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%mod;
 61 }
 62 
 63 void pushdown(int rt,int m)//下放lazy标记
 64 {
 65     if(lazy[rt]){
 66         lazy[rt<<1]+=lazy[rt];
 67         lazy[rt<<1|1]+=lazy[rt];
 68         sum[rt<<1]+=(m-(m>>1))*lazy[rt],sum[rt<<1]%=mod;
 69         sum[rt<<1|1]+=(m>>1)*lazy[rt],sum[rt<<1|1]%=mod;
 70         lazy[rt]=0;
 71     }
 72 }
 73 
 74 void build(int l,int r,int rt)
 75 {
 76     lazy[rt]=0;
 77     if(l==r){
 78         sum[rt]=wt[l],sum[rt]%=mod;//新的编号点权
 79         return ;
 80     }
 81 
 82     int m=(l+r)>>1;
 83     build(lson);
 84     build(rson);
 85     pushup(rt);
 86 }
 87 
 88 void update(int L,int R,int c,int l,int r,int rt)//区间更新
 89 {
 90     if(L<=l&&r<=R){
 91         lazy[rt]+=c;
 92         sum[rt]+=c*(r-l+1),sum[rt]%=mod;
 93         return ;
 94     }
 95 
 96     pushdown(rt,r-l+1);
 97     int m=(l+r)>>1;
 98     if(L<=m) update(L,R,c,lson);
 99     if(R> m) update(L,R,c,rson);
100     pushup(rt);
101 }
102 
103 int query(int L,int R,int l,int r,int rt)
104 {
105     if(L<=l&&r<=R){
106         return sum[rt];
107     }
108 
109     int ret=0;
110     pushdown(rt,r-l+1);
111     int m=(l+r)>>1;
112     if(L<=m) ret+=query(L,R,lson),ret%=mod;
113     if(R> m) ret+=query(L,R,rson),ret%=mod;
114     return ret;
115 }
116 
117 //树链剖分部分
118 void dfs1(int u,int father)
119 {
120     siz[u]=1;//记录每个节点子树大小
121     fa[u]=father;//标记节点的父亲
122     dep[u]=dep[father]+1;//标记深度
123     for(int i=head[u];~i;i=edge[i].next){
124         int v=edge[i].to;
125         if(v==father) continue;//如果连接的是当前节点的父亲节点,则不处理
126         dfs1(v,u);
127         siz[u]+=siz[v];//直接子树节点相加,当前节点的size加上子节点的size
128         if(siz[v]>siz[son[u]]){//如果没有设置过重节点son或者子节点v的size大于之前记录的重节点son,进行更新,保存重儿子
129             son[u]=v;//标记u的重儿子为v
130         }
131     }
132 }
133 
134 void dfs2(int u,int tp)
135 {
136     top[u]=tp;//标记每个重链的顶端
137     tid[u]=++cnt;//每个节点剖分以后的新编号(dfs的执行顺序)
138     wt[cnt]=w[u];//新编号的对应权值
139     if(!son[u]) return ;//如果当前节点没有处在重链上,则不处理,否则就将这条重链上的所有节点都设置成起始的重节点
140     dfs2(son[u],tp);//搜索下一个重儿子
141     for(int i=head[u];~i;i=edge[i].next){
142         int v=edge[i].to;
143         if(v==fa[u]||v==son[u]) continue;//处理轻儿子,如果连接节点不是当前节点的重节点并且也不是u的父节点,则将其的top设置成自己,进一步递归
144         dfs2(v,v);//每一个轻儿子都有一个从自己开始的链
145     }
146 }
147 
148 void update_path(int x,int y,int k)//路径更新
149 {
150     k%=mod;
151     while(top[x]!=top[y]){
152         if(dep[top[x]]<dep[top[y]]) swap(x,y);//使x深度较大
153         update(tid[top[x]],tid[x],k,1,n,1);
154         x=fa[top[x]];
155     }
156 
157     if(dep[x]>dep[y]) swap(x,y);//使x深度较小
158     update(tid[x],tid[y],k,1,n,1);
159 }
160 
161 int getsum_path(int x,int y)//路径求和
162 {
163     int ans=0;
164     while(top[x]!=top[y]){//当两个点不在同一条链上
165         if(dep[top[x]]<dep[top[y]]) swap(x,y);//使x深度较大
166         ans+=query(tid[top[x]],tid[x],1,n,1),ans%=mod;
167         x=fa[top[x]];//x跳到x所在链顶端的这个点的上面一个点
168     }
169 
170     if(dep[x]>dep[y]) swap(x,y);//当两个点处于同一条链,使x深度较小
171     ans+=query(tid[x],tid[y],1,n,1),ans%=mod;
172     return ans;
173 }
174 
175 void update_subtree(int x,int k)//子树更新
176 {
177     update(tid[x],tid[x]+siz[x]-1,k,1,n,1);//子树区间右端点为tid[x]+siz[x]-1
178 }
179 
180 int getsum_subtree(int x)//子树求和
181 {
182     return query(tid[x],tid[x]+siz[x]-1,1,n,1);
183 }
184 
185 int main()
186 {
187     scanf("%d%d%d%d",&n,&m,&r,&mod);
188     init();
189     for(int i=1;i<=n;i++)
190         scanf("%d",&w[i]);//点权
191     for(int i=1;i<=n-1;i++){
192         int u,v;
193         scanf("%d%d",&u,&v);
194         add(u,v);
195         add(v,u);
196     }
197     dfs1(r,0);//根节点
198     dfs2(r,r);
199     build(1,n,1);
200     while(m--){
201         int op,x,y,z;
202         scanf("%d",&op);
203         if(op==1){
204             scanf("%d%d%d",&x,&y,&z);
205             update_path(x,y,z);
206         }
207         else if(op==2){
208             scanf("%d%d",&x,&y);
209             printf("%d\n",getsum_path(x,y));
210         }
211         else if(op==3){
212             scanf("%d%d",&x,&z);
213             update_subtree(x,z);
214         }
215         else if(op==4){
216             scanf("%d",&x);
217             printf("%d\n",getsum_subtree(x));
218         }
219     }
220     return 0;
221 }

溜了溜了。。。

猜你喜欢

转载自www.cnblogs.com/ZERO-/p/9762991.html
今日推荐