树剖是干什么的?
考虑这样的情况:有一棵树,现在要对其进行路径(两节点间)操作、子树操作,例如将路径上(子树上)的所有节点全部加上一个值、求和等等。
直接暴力操作固然是可行的,但时间肯定是个问题。
这时我们想,如果能用数据结构维护树上节点就好了,但是树的“张牙舞爪”的样子,使得这个操作难以完成。
于是,树剖应运而生。树剖的作用是,把树拆分成若干条“链”,显然,“链”是线性的,方便用数据结构维护。
具体的拆分方式名为“轻重链剖分”。使用这种拆分方式,可以使任一节点到根的路径上,经过的重链和轻边数量均不超过logn,这里的n指的是树的节点总数。关于具体概念及复杂度证明请自行百度,此处不再赘述。
树剖过程需要求得的一系列数组如下:
fa[]:节点的父亲节点(根节点为无);
dep[]:节点深度;
size[]:以节点为根的子树的节点总数;
son[]:节点的重儿子编号;
top[]:节点所处重链的顶端节点;
dfsx[]:按照重链优先遍历出的DFS序;
pos[]:节点在数据结构中的位置。
初始化过程:
1.存图;
2.进行第一遍DFS,自根向下遍历(这个过程类似于无根树转有根树),求出fa[]、dep[]、size[]、son[];
3.进行第二遍DFS,按照重链优先的顺序遍历,求出top[]、dfsx[],这时的DFS序满足:同一条重链或同一棵子树上的节点在DFS序中是连续的;
4.根据DFS序计算pos[],细节请见代码,我把这个过程叫做“反向映射”;
5.按照DFS序,将节点值存入数据结构(我用的是线段树)。
查询及修改:
1.路径操作,需要知道两节点的LCA,但是不必单独去求,只需按照重链和轻边向上跳即可到达LCA(轻边跳一下,重链跳到top的父节点,话说我也不明白这样搞为什么是对的),沿路修改/查询即可,复杂度log^2n,细节请见代码。
2.子树操作,因为同一棵子树上的节点在数据结构中是连续的,所以借助size值,仅修改/查询一次即可,复杂度logn。
最后提醒一句,适当取模,取少了计算结果会溢出,取多了会TLE。。。
代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cmath> 5 #include<ctime> 6 #include<cstdlib> 7 8 #include<string> 9 #include<stack> 10 #include<queue> 11 #include<vector> 12 #include<algorithm> 13 #include<map> 14 #include<set> 15 16 #define ll long long 17 18 using namespace std; 19 20 inline void readint(int &x){ 21 x=0; 22 char t=getchar(); 23 bool f=0; 24 25 while(t<'0' || t>'9'){ 26 if(t=='-')f=1; 27 t=getchar(); 28 } 29 30 while(t>='0' && t<='9'){ 31 x=(x<<3)+(x<<1)+t-'0'; 32 t=getchar(); 33 } 34 35 if(f)x=-x; 36 } 37 38 inline void readll(ll &x){ 39 x=0; 40 char t=getchar(); 41 bool f=0; 42 43 while(t<'0' || t>'9'){ 44 if(t=='-')f=1; 45 t=getchar(); 46 } 47 48 while(t>='0' && t<='9'){ 49 x=(x<<3)+(x<<1)+t-'0'; 50 t=getchar(); 51 } 52 53 if(f)x=-x; 54 } 55 56 ll a[200010]; //点值 57 58 int v[200010]; 59 int first[200010]; 60 int next[200010]; 61 int ord=0; //邻接表 62 63 int fa[200010]; 64 int dep[200010]; 65 int size[200010]; 66 int son[200010]; 67 68 int top[200010]; 69 int dfsx[200010]; 70 int xu=0; 71 72 int pos[200010]; 73 74 int n,m,root,mod,i; 75 int f,x,y; 76 ll k; 77 78 inline void setup(){ 79 memset(first,0,sizeof(first)); 80 memset(next,0,sizeof(next)); 81 82 memset(son,0,sizeof(son)); 83 } 84 85 inline void addedge(){ 86 ord++; 87 v[ord]=y; 88 next[ord]=first[x]; 89 first[x]=ord; 90 91 ord++; 92 v[ord]=x; 93 next[ord]=first[y]; 94 first[y]=ord; 95 } 96 97 void dfs1(int); 98 void dfs2(int); 99 100 inline void path_update(int,int); 101 inline ll path_query(int,int); 102 inline void son_update(int); 103 inline ll son_query(int); 104 105 struct sgt{ 106 ll sum[800010]; 107 ll addv[800010]; 108 109 void build(int o,int l,int r){ 110 addv[o]=0; 111 112 if(l==r)sum[o]=a[dfsx[l]]; 113 else{ 114 int mid=(l+r)>>1; 115 int lson=o<<1; 116 int rson=lson|1; 117 118 build(lson,l,mid); 119 build(rson,mid+1,r); 120 121 sum[o]=(sum[lson]+sum[rson])%mod; 122 } 123 } 124 125 void pushdown(int o,int l,int r,int mid,int lson,int rson){ 126 addv[lson]=(addv[lson]+addv[o])%mod; 127 addv[rson]=(addv[rson]+addv[o])%mod; 128 129 sum[lson]=(sum[lson]+addv[o]*(mid-l+1))%mod; 130 sum[rson]=(sum[rson]+addv[o]*(r-mid))%mod; 131 132 addv[o]=0; 133 } 134 135 void update(int o,int l,int r,int a,int b,int x){ 136 if(l>=a && r<=b){ 137 addv[o]=(addv[o]+x)%mod; 138 sum[o]=(sum[o]+x*(r-l+1))%mod; 139 return; 140 } 141 else{ 142 int mid=(l+r)>>1; 143 int lson=o<<1; 144 int rson=lson|1; 145 146 if(addv[o])pushdown(o,l,r,mid,lson,rson); 147 148 if(a<=mid)update(lson,l,mid,a,b,x); 149 if(b>mid)update(rson,mid+1,r,a,b,x); 150 151 sum[o]=(sum[lson]+sum[rson])%mod; 152 } 153 } 154 155 ll query(int o,int l,int r,int a,int b){ 156 if(l>=a && r<=b)return sum[o]; 157 else{ 158 int mid=(l+r)>>1; 159 int lson=o<<1; 160 int rson=lson|1; 161 ll ans=0; 162 163 if(addv[o])pushdown(o,l,r,mid,lson,rson); 164 165 if(a<=mid)ans+=query(lson,l,mid,a,b); 166 if(b>mid)ans=(ans+query(rson,mid+1,r,a,b))%mod; 167 168 return ans; 169 } 170 } 171 } tree; 172 173 int main(){ 174 setup(); 175 176 readint(n);readint(m);readint(root);readint(mod); 177 178 for(register int i=1;i<=n;i++)readll(a[i]); 179 180 for(register int i=1;i<n;i++){ 181 readint(x);readint(y); 182 addedge(); 183 } 184 185 fa[root]=0; 186 dep[root]=1; 187 dfs1(root); 188 189 top[root]=root; 190 dfs2(root); 191 192 for(register int i=1;i<=n;i++)pos[dfsx[i]]=i; 193 194 tree.build(1,1,n); 195 196 while(m--){ 197 readint(f); 198 199 switch(f){ 200 case 1:{ 201 readint(x);readint(y);readll(k); 202 path_update(x,y); 203 break; 204 } 205 case 2:{ 206 readint(x);readint(y); 207 printf("%lld ",path_query(x,y)); 208 break; 209 } 210 case 3:{ 211 readint(x);readll(k); 212 son_update(x); 213 break; 214 } 215 case 4:{ 216 readint(x); 217 printf("%lld ",son_query(x)); 218 break; 219 } 220 } 221 } 222 223 return 0; 224 } 225 226 void dfs1(int x){ //fa,dep在上层处理 227 size[x]=1; 228 int e=first[x],u=v[e],maxson=0; 229 230 while(e){ 231 if(u==fa[x]){ 232 e=next[e]; 233 u=v[e]; 234 continue; 235 } 236 237 fa[u]=x; 238 dep[u]=dep[x]+1; 239 240 dfs1(u); 241 242 size[x]+=size[u]; 243 if(size[u]>maxson){ 244 maxson=size[u]; 245 son[x]=u; 246 } 247 248 e=next[e]; 249 u=v[e]; 250 } 251 } 252 253 void dfs2(int x){ //top在上层处理 254 xu++; 255 dfsx[xu]=x; 256 257 if(son[x]){ 258 top[son[x]]=top[x]; 259 dfs2(son[x]); 260 } 261 262 int e=first[x],u=v[e]; 263 264 while(e){ 265 if(u==fa[x] || u==son[x]){ 266 e=next[e]; 267 u=v[e]; 268 continue; 269 } 270 271 top[u]=u; 272 dfs2(u); 273 274 e=next[e]; 275 u=v[e]; 276 } 277 } 278 279 inline void path_update(int x,int y){ 280 int tx=top[x],ty=top[y]; 281 282 while(tx!=ty){ 283 if(dep[tx]>dep[ty]){ 284 tree.update(1,1,n,pos[tx],pos[x],k); 285 x=fa[tx]; 286 tx=top[x]; 287 } 288 else{ 289 tree.update(1,1,n,pos[ty],pos[y],k); 290 y=fa[ty]; 291 ty=top[y]; 292 } 293 } 294 295 if(pos[x]<pos[y])tree.update(1,1,n,pos[x],pos[y],k); 296 else tree.update(1,1,n,pos[y],pos[x],k); 297 } 298 299 inline ll path_query(int x,int y){ 300 ll ans=0; 301 int tx=top[x],ty=top[y]; 302 303 while(tx!=ty){ 304 if(dep[tx]>dep[ty]){ 305 ans+=tree.query(1,1,n,pos[tx],pos[x]); 306 x=fa[tx]; 307 tx=top[x]; 308 } 309 else{ 310 ans+=tree.query(1,1,n,pos[ty],pos[y]); 311 y=fa[ty]; 312 ty=top[y]; 313 } 314 } 315 316 if(pos[x]<pos[y])ans+=tree.query(1,1,n,pos[x],pos[y]); 317 else ans+=tree.query(1,1,n,pos[y],pos[x]); 318 319 return ans%mod; 320 } 321 322 inline void son_update(int root){ 323 tree.update(1,1,n,pos[root],pos[root]+size[root]-1,k); 324 } 325 326 inline ll son_query(int root){ 327 return tree.query(1,1,n,pos[root],pos[root]+size[root]-1); 328 }