首页 > 代码库 > 洛谷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 247 3 7 8 0 1 21 53 14 13 4 23 2 24 51 5 1 32 1 3
输出样例#1:
221

说明

时空限制: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 }

 

洛谷P3384 【模板】树链剖分