洛谷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
",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
",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 }
原文地址:https://www.cnblogs.com/SilverNebula/p/6025052.html