洛谷P3384 模板树链剖分
Posted SilverNebula
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷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 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
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 }
以上是关于洛谷P3384 模板树链剖分的主要内容,如果未能解决你的问题,请参考以下文章