樹鏈剖分模板

 

落谷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  */

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章