树链剖分简析
落谷3384
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 #include<queue> 7 #include<stack> 8 #include<deque> 9 #include<map> 10 #include<iostream> 11 using namespace std; 12 typedef long long LL; 13 const double pi=acos(-1.0); 14 const double e=exp(1); 15 //const int MAXN =2e5+10; 16 const LL N = 100010*4; 17 18 struct node 19 { 20 LL l,r; 21 LL mid() { 22 return (l + r) / 2; 23 } 24 }node[N]; 25 LL sum[N]; 26 LL add[N]; 27 28 LL con[N]; 29 LL head[N]; 30 struct edge{ 31 LL to; 32 LL next; 33 LL w; 34 }edge[N]; 35 36 37 LL f[N]; //结点i的父结点 38 LL d[N]; // 结点i的深度 39 LL size[N]; // 结点i子树的规模(结点数) 40 LL son[N]; // 结点i的重结点 41 LL top[N]; // 结点i所在链的顶端结点 42 LL id[N]; // 树中结点剖分后对应的数组下标 43 LL rk[N]; // 剖分后数组下标对应的树上的结点 44 45 LL cnt1 = 1, cnt = 0; 46 LL n,m,r,p; 47 48 void PushUp(LL i) 49 { 50 //sum[i] = max(sum[i << 1], sum[i << 1 | 1]); 51 sum[i] = sum[i << 1] + sum[i << 1 | 1]; 52 } 53 54 void PushDown(LL i,LL L) // LΪÇøŒä³€¶È £šÑÓ³Ù±êŒÇ£© 55 { 56 if(add[i]) 57 { 58 add[i << 1] += add[i] % p; 59 add[i << 1 | 1] += add[i] % p; 60 sum[i << 1] += add[i] * (L - (L >> 1)) % p; // Ò׎í 61 sum[i << 1 | 1] += add[i] * (L >> 1) % p; 62 add[i] = 0; 63 } 64 } 65 66 void Build(LL l, LL r, LL i) 67 { 68 //cout << " l: " << l << " r: " << r << endl; 69 node[i].l = l; 70 node[i].r = r; 71 sum[i] = 0; 72 add[i] = 0; 73 if(l == r) 74 { 75 // cout << " l: " << l << " r: " << r << " " << rk[cnt1] << endl; 76 sum[i] = con[rk[cnt1++]]; 77 return ; 78 //scanf("%d",&sum[i]); 79 } 80 LL m = node[i].mid(); 81 82 Build(l, m, i << 1); 83 Build(m+1, r, i << 1 | 1); 84 85 PushUp(i); 86 } 87 88 LL Query(LL l, LL r, LL i) 89 { 90 if(node[i].l == l && node[i].r == r) 91 { 92 return sum[i]; 93 } 94 PushDown(i, node[i].r - node[i].l + 1); 95 LL m = node[i].mid(); 96 LL ans = 0; 97 if(r <= m) 98 { 99 ans += Query(l, r, i << 1) % p; 100 } 101 else 102 { 103 if(l > m) 104 { 105 ans += Query(l, r, i << 1 | 1) % p; 106 } 107 else 108 { 109 ans += Query(l, m, i << 1) % p; 110 ans += Query(m+1, r, i << 1 | 1) % p; 111 } 112 } 113 return ans; 114 } 115 116 void Update(LL l, LL r, LL x, LL i) 117 { 118 // cout << " ^^ " << node[i].l << " " << node[i].r << endl; 119 if(node[i].l == l && node[i].r == r) 120 { 121 sum[i] += x * (r - l + 1) % p; 122 add[i] += x % p; 123 return ; 124 } 125 PushDown(i,node[i].r - node[i].l + 1); 126 LL m = node[i].mid(); 127 if(r <= m) 128 { 129 Update(l, r, x, i << 1); 130 } 131 else 132 { 133 if(l > m) 134 { 135 Update(l, r, x, i << 1 | 1); 136 } 137 else 138 { 139 Update(l, m, x, i << 1); 140 Update(m+1, r, x, i << 1 | 1); 141 } 142 } 143 PushUp(i); 144 } 145 146 147 148 // 处理size,son,f,d数组 149 void dfs1(LL u, LL fa, LL depth) //当前节点、父节点、层次深度 150 { 151 LL i; 152 f[u] = fa; 153 d[u] = depth; 154 size[u] = 1; //这个点本身size = 1; 155 156 for( i = head[u]; i != -1; i = edge[i].next) 157 { 158 LL v = edge[i].to; 159 if(v == fa) 160 continue; 161 dfs1(v, u, depth + 1); 162 size[u] += size[v]; 163 if(size[v] > size[son[u]]) //son[u]数组为u的重儿子 164 { 165 son[u] = v; 166 } 167 } 168 } 169 //dfs1(root, 0, 1); // 处理结点子树的规模,每个结点的重儿子,结点所在的深度 170 171 172 173 // 处理top,id,rk数组 174 void dfs2(LL u, LL t) //当前结点、重链顶端 175 { 176 top[u] = t; 177 id[u] = ++cnt; //标记dfs序 178 rk[cnt] = u; // 序号cnt对应结点u 179 if(!son[u]) // 当点u为叶子节点时,代表一条重链剖分完毕。 180 return ; // 返回到倒数第二个结点,从该结点出发,剖分另一条链。 181 dfs2(son[u],t); 182 183 for(LL i = head[u]; i != -1; i = edge[i].next) //从该结点所连接的结点出发,进行剖分 184 { 185 LL v = edge[i].to; 186 if(v != son[u] && v != f[u]) // 链头要求不能是结点u的重儿子(因为该结点已经被重链占用了) 187 // 也不能是结点u的父结点 188 dfs2(v,v); //每条链都是以轻结点为开头,后面链的都是重儿子。所以结点v的链头是它本身。 189 } 190 191 } 192 193 void U_xy(LL x, LL y, LL z) 194 { 195 LL ans = 0, fx = top[x], fy = top[y]; 196 while(fx != fy) 197 { 198 if(d[fx] >= d[fy]) 199 { 200 if(id[x] <= id[fx]) 201 Update(id[x], id[fx], z, 1); 202 else 203 { 204 Update(id[fx], id[x], z, 1); 205 } 206 207 x = f[fx]; 208 fx = top[x]; 209 } 210 else 211 { 212 if(id[y] <= id[fy]) 213 Update(id[y], id[fy], z, 1); 214 else 215 { 216 Update(id[fy], id[y], z, 1); 217 } 218 219 y = f[fy]; 220 fy = top[y]; 221 } 222 } 223 224 if(id[x] <= id[y]) 225 { 226 Update(id[x], id[y], z, 1); 227 } 228 else 229 { 230 Update(id[y], id[x], z, 1); 231 } 232 233 } 234 235 LL Q_xy(LL x, LL y) 236 { 237 LL ans = 0, fx = top[x], fy = top[y]; //fx代表x的链头结点,fy同理。 238 while(fx != fy) 239 { 240 // cout << " ^^ " << "x: " << x << " y: " << y << "fx: " << fx << "fy: " << fy << endl; 241 // cout << id[x] << " " << id[fx] << " ** " << endl; 242 if(d[fx] >= d[fy]) // 如果x的深度比y的深度大,所以应让x向上跳 243 { 244 if(id[x] <= id[fx]) 245 ans += Query(id[x], id[fx], 1) % p; // 因为剖分后每条链上的结点都是线性存储的,所以x到x链头的父节点的距离,为两者之间的距离的差。 246 else 247 { 248 ans += Query(id[fx], id[x], 1) % p; 249 } 250 251 x = f[fx]; // x更新为x链头结点的父节点 252 fx = top[x]; // fx更新为新x结点的链头结点, 然后再去和fy比较,判断x,y是否在同一条链上了。 253 } 254 else 255 { 256 if(id[y] <= id[fy]) 257 ans += Query(id[y], id[fy], 1) % p; 258 else 259 { 260 ans += Query(id[fy], id[y], 1) % p; 261 } 262 263 y = f[fy]; 264 fy = top[y]; 265 } 266 } 267 268 if(id[x] <= id[y]) 269 { 270 ans += Query(id[x], id[y], 1) % p; 271 } 272 else 273 { 274 ans += Query(id[y], id[x], 1) % p; 275 } 276 277 return ans % p; 278 } 279 280 void U_x(LL x, LL z) 281 { 282 // cout << "id[x]: " << id[x] << " size: " << size[x] << endl; 283 // cout << " ** " << Query(id[x],id[x] + size[x] - 1,1) << endl;; 284 Update(id[x], id[x] + size[x] - 1, z, 1); 285 // cout << " ** " << Query(id[x],id[x] + size[x] - 1,1) << endl;; 286 287 } 288 289 LL Q_x(LL x) 290 { 291 return Query(id[x], id[x] + size[x] - 1, 1) % p; 292 } 293 294 295 int main() 296 { 297 298 LL i,j,a,b,x,y,z; 299 300 scanf("%lld%lld%lld%lld",&n,&m,&r,&p); 301 for(i = 1; i <= n; i ++) 302 { 303 scanf("%lld",&con[i]); 304 } 305 306 memset(head,-1,sizeof(head)); 307 LL cnt2 = 0; 308 for(i = 1; i < n; i++) 309 { 310 scanf("%lld%lld",&a,&b); 311 edge[cnt2].to = b; 312 edge[cnt2].next= head[a]; 313 head[a] = cnt2 ++; 314 315 316 edge[cnt2].to = a; 317 edge[cnt2].next = head[b]; 318 head[b] = cnt2 ++; 319 } 320 dfs1(r, 0, 1); 321 dfs2(r, r); 322 323 324 Build(1, n, 1); 325 326 /* 327 cout << "** "; 328 for(i = 1; i <= cnt; i++) 329 { 330 cout << rk[i] << " " ; 331 } 332 cout << endl; 333 */ 334 while(m--) 335 { 336 scanf("%lld",&j); 337 // cout << "j: " << j << endl; 338 if(j == 1) 339 { 340 scanf("%lld%lld%lld",&x,&y,&z); 341 U_xy(x,y,z); 342 } 343 else if(j == 2) 344 { 345 scanf("%lld%lld",&x,&y); 346 printf("%lld\n", Q_xy(x,y) % p); 347 } 348 else if(j == 3) 349 { 350 scanf("%lld%lld",&x,&z); 351 U_x(x,z); 352 } 353 else if(j == 4) 354 { 355 scanf("%lld",&x); 356 printf("%lld\n", Q_x(x) % p); 357 } 358 } 359 return 0; 360 } 361 362 363 364 365 366 367 368 369 /* 370 888888888888888888888888888 36375 68190 63528 ** 371 888888888888888888888888888 66907 32530 32528 ** 372 373 374 888888888888888888888888888 36375 31713 68190 63528 ** 375 888888888888888888888888888 66907 86992 32530 32528 ** 376 377 */