洛谷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<=10,M<=10

对于70%的数据:N<=1000,M<=1000

对于100%的数据:N<=100000,M<=100000

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

样例说明:

树的结构如下:

各个操作如下:

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

半夜强行自学树剖,明明半个月前还根本看不懂,这次居然神tm顿悟了。もう何も怖くない(じゃねいよ)

大概就是把树边对应成线段树上的结点,一长串连着的边(重链)结点依次分布,散边(轻链)结点有规律分散,然后每次操作就和LCA倍增一样(并不)一路爬到根节点统计答案……

然后一通乱搞。

  1 /*by SilverN*/
  2 #include<iostream>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<cstdio>
  6 #include<cmath>
  7 #define LL long long
  8 using namespace std;
  9 const int mxn=100010;
 10 int read(){
 11     int x=0,f=1;char ch=getchar();
 12     while(ch<‘0‘ || ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
 13     while(ch>=‘0‘ && ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
 14     return x*f;
 15 }
 16 //读入优化
 17 struct edge{int v,nxt;}e[mxn<<1];
 18 int hd[mxn],mct=0;
 19 void add_edge(int u,int v){e[++mct].v=v;e[mct].nxt=hd[u];hd[u]=mct;}
 20 //邻接表
 21 struct node{
 22     int fa,son;
 23     int size,dep,top;
 24     int w,e;
 25 }tr[mxn];
 26 //树剖结点
 27 struct segtree{
 28     LL smm,mk;
 29 }st[mxn<<2];
 30 int sz=0;
 31 //线段树
 32 int n,M,rt,mod;
 33 int w[mxn];//初始值
 34 //
 35 void DFS1(int u){
 36     tr[u].size=1;tr[u].son=0;
 37     for(int i=hd[u];i;i=e[i].nxt){
 38         int v=e[i].v;
 39         if(v==tr[u].fa)continue;
 40         tr[v].fa=u;
 41         tr[v].dep=tr[u].dep+1;
 42         DFS1(v);
 43         tr[u].size+=tr[v].size;
 44         if(tr[v].size>tr[tr[u].son].size)
 45             tr[u].son=v;
 46     }
 47     return;
 48 }
 49 void DFS2(int u,int top){//当前点,当前链的顶点
 50     tr[u].top=top;
 51     tr[u].w=++sz;//把树边挂上线段树
 52     if(tr[u].son){
 53         DFS2(tr[u].son,top);//扩展搭建重链
 54         for(int i=hd[u];i;i=e[i].nxt){
 55             int v=e[i].v;
 56             if(v!=tr[u].fa && v!=tr[u].son)
 57                 DFS2(v,v);//搭建轻链
 58         }
 59     }
 60     tr[u].e=sz;//当前点对应的线段树结尾
 61     return;
 62 }
 63 void update(int L,int R,LL w,int l,int r,int k){//区间加值
 64      if(L<=l && r<=R){
 65          st[k].mk+=w;
 66          st[k].smm+=(r-l+1)*w;
 67          st[k].smm%=mod;
 68          return;
 69     }
 70     int mid=(l+r)>>1;
 71     if(st[k].mk){
 72         st[k<<1].mk+=st[k].mk;
 73         st[k<<1].smm+=st[k].mk*(mid-l+1);
 74         st[k<<1].smm%=mod;
 75         st[k<<1|1].mk+=st[k].mk;
 76         st[k<<1|1].smm+=st[k].mk*(r-mid);
 77         st[k<<1|1].smm%=mod;
 78         st[k].mk=0;
 79     }
 80     if(L<=mid)update(L,R,w,l,mid,k<<1);
 81     if(R>mid)update(L,R,w,mid+1,r,k<<1|1);
 82     st[k].smm=(st[k<<1].smm+st[k<<1|1].smm)%mod;
 83     return;
 84 }
 85 int query(int L,int R,int l,int r,int k){
 86     if(L<=l && r<=R)return st[k].smm;
 87     int mid=(l+r)>>1;
 88     if(st[k].mk){
 89         st[k<<1].mk+=st[k].mk;
 90         st[k<<1].smm+=st[k].mk*(mid-l+1);
 91         st[k<<1].smm%=mod;
 92         st[k<<1|1].mk+=st[k].mk;
 93         st[k<<1|1].smm+=st[k].mk*(r-mid);
 94         st[k<<1|1].smm%=mod;
 95         st[k].mk=0;
 96     }
 97     LL res=0;
 98     if(L<=mid)res=(res+query(L,R,l,mid,k<<1))%mod;
 99     if(R>mid)res=(res+query(L,R,mid+1,r,k<<1|1))%mod;
100     return res%mod;
101 }
102 int find(int x,int y){
103     int f1=tr[x].top,f2=tr[y].top;
104     int ans=0;
105     while(f1!=f2){
106         if(tr[f1].dep<tr[f2].dep){
107             ans+=query(tr[f2].w,tr[y].w,1,n,1);
108             y=tr[f2].fa;
109             f2=tr[y].top;
110         }
111         else{
112             ans+=query(tr[f1].w,tr[x].w,1,n,1);
113             x=tr[f1].fa;
114             f1=tr[x].top;
115         }
116         ans%=mod;
117     }
118     if(tr[x].dep<tr[y].dep)return ans+query(tr[x].w,tr[y].w,1,n,1);
119     return ans+query(tr[y].w,tr[x].w,1,n,1);
120 }
121 void add(int x,int y,int k){//x到y的路径加k
122     int f1=tr[x].top,f2=tr[y].top;
123     while(f1!=f2){
124         if(tr[f1].dep<tr[f2].dep){
125             update(tr[f2].w,tr[y].w,k,1,n,1);
126             y=tr[f2].fa;
127             f2=tr[y].top;
128         }
129         else{
130             update(tr[f1].w,tr[x].w,k,1,n,1);
131             x=tr[f1].fa;
132             f1=tr[x].top;
133         }
134     }
135     if(tr[x].dep<tr[y].dep) update(tr[x].w,tr[y].w,k,1,n,1);
136     else update(tr[y].w,tr[x].w,k,1,n,1);
137     return;
138 }
139 //
140 int main(){
141     n=read();M=read();rt=read();mod=read();
142     int i,j;
143     for(i=1;i<=n;i++){w[i]=read();}
144     int x,y;
145     for(i=1;i<n;i++){
146         x=read();y=read();
147         add_edge(x,y);
148         add_edge(y,x);
149     }
150     sz=tr[0].size=tr[rt].dep=0;
151     //
152     DFS1(rt);
153     DFS2(rt,rt);
154     for(i=1;i<=n;i++)update(tr[i].w,tr[i].w,w[i],1,n,1);
155     int op;
156     for(i=1;i<=M;i++){
157         op=read();x=read();
158         switch(op){
159             case 4:{
160                 printf("%d\n",query(tr[x].w,tr[x].e,1,n,1)%mod);
161                 break;
162             }
163             case 3:{
164                 y=read();
165                 update(tr[x].w,tr[x].e,y,1,n,1);
166                 break;
167             }
168             case 2:{
169                 y=read();
170                 printf("%d\n",find(x,y)%mod);
171                 break;
172             }
173             case 1:{
174                 y=read();j=read();
175                 add(x,y,j);
176                 break;
177             }
178         }
179     }
180     return 0;
181 }
时间: 2024-10-12 14:39:19

洛谷P3384 【模板】树链剖分的相关文章

[luogu P3384] [模板]树链剖分

[luogu 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个正整数

洛谷.4114.Qtree1(树链剖分)

题目链接 模板题都错了这么多次.. //边权赋到点上 树剖模板 //注意LCA.链的顶端不能统计到答案! #include <cstdio> #include <cctype> #include <algorithm> #define gc() getchar() #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 const int N=1e5+5; int n,m,cnt,ep[N],W[N],

洛谷 P3979 遥远的国度(树链剖分)

题目描述 修改某条路径上的值以及询问子树的最小值都是最树剖的基础操作,那么如何实现换根呢? 考虑一下三种情况: 1.rot=询问的子树x,答案就是整棵树的最小值 2.rot在x的子树里,只有rot到x这一条链上的的节点的子树会变 找到x在rot方向上的子节点,答案就是除去这棵子树的最小值 3.rot不在x的子树里,那么rot是谁对x的子树没有影响,答案不变 那么就在询问时分类讨论一下就好了 #include<complex> #include<cstdio> using names

luoguP3384 [模板]树链剖分

luogu P3384 [模板]树链剖分 题目 #include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #include<cstring> #include<iomanip> #include<algorithm> #include<ctime> #include<queue> #define rg register

【Luogu P3384】树链剖分模板

树链剖分的基本思想是把一棵树剖分成若干条链,再利用线段树等数据结构维护相关数据,可以非常暴力优雅地解决很多问题. 树链剖分中的几个基本概念: 重儿子:对于当前节点的所有儿子中,子树大小最大的一个儿子就是重儿子(子树大小相同的则随意取一个) 轻儿子:不是重儿子就是轻儿子 重边:连接父节点和重儿子的边 轻边:连接父节点和轻儿子的边 重链:相邻重边相连形成的链 值得注意的还有以下几点: 叶子节点没有重儿子也没有轻儿子: 对于每一条重链,其起点必然是轻儿子: 单独一个轻叶子节点也是一条重链: 结合上面三

[模板]树链剖分

原题链接:https://www.luogu.org/problemnew/show/P3384 树链剖分+线段树,备用. 等待补充详细解释中. /* 1 x y z x到y最短路径上加上z 2 x y 求x到y的最短路径上的节点值之和 3 x z 以x为根节点的子树内的所有节点值都加上z 4 x 以x为根节点的所有节点值之和 */ #include<cstdio> void read(int &y) { y=0;char x=getchar();int f=1; while(x<

模板 树链剖分BFS版本

//点和线段树都从1开始 //边使用vector vector<int> G[maxn]; int dfs_clock,que[maxn*2],num[maxn],iii[maxn],b[maxn],a[maxn],top[maxn],deep[maxn],fa[maxn],idx[maxn]; //采用静态链表 //a[i] 是初始时树上每个点的权值 //b[i] 是经过bfs后每个点的权值 //idx[i] 是每个点在全局线段树中的下标 void build_List() { int ft

模板——树链剖分

放个板子而已.. #include<stdio.h> #include<algorithm> #define ls root<<1 #define rs root<<1|1 using namespace std; typedef long long ll; int n,m,r;ll res,p; const int MAXN = 100005; ll a[MAXN]; struct edge{ int v,nxt; }e[MAXN<<1]; s

[模板] 树链剖分找LCA

#include <cstdio> #include <cstring> #define MAX 500005 int d[MAX],fa[MAX],size[MAX],top[MAX],son[MAX]; int N,M,S,tot=0; int head[MAX]; struct edge{ int v,next; }G[MAX<<1]; inline void add(int u,int v){ G[++tot].v=v;G[tot].next=head[u];h

树链剖分[模板](洛谷 P3384)

洛谷·[模板]树链剖分 写在前面 首先,在学树链剖分之前最好先把 LCA.树形DP.DFS序 这三个知识点学了 如果这三个知识点没掌握好的话,树链剖分难以理解也是当然的. 树链剖分 树链剖分 就是对一棵树分成几条链,把树形变为线性,减少处理难度 概念 dfs1() dfs2() 对剖过后的树建线段树 处理问题 概念 重儿子:对于每一个非叶子节点,它的儿子中 儿子数量最多的那一个儿子 为该节点的重儿子 轻儿子:对于每一个非叶子节点,它的儿子中 非重儿子 的剩下所有儿子即为轻儿子 叶子节点没有重儿子