首页 > 代码库 > 洛谷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取模)
输入输出样例
5 5 2 247 3 7 8 0 1 21 53 14 13 4 23 2 24 51 5 1 32 1 3
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 【模板】树链剖分