洛谷 P3384 【模板】樹鏈剖分
樹鏈剖分
將一棵樹的每個節點到它所有子節點中子樹和(所包含的點的個數)最大的那個子節點的這條邊標記為“重邊”。
將其他的邊標記為“輕邊”。
若果一個非根節點的子樹的大小不小於任意一個他兄弟節點的子數大小(若有多個就看心情選取其中的一個),那麽它到它父節點的連邊為重邊,這個節點為重子節點,否則,它到它父節點的連邊為輕邊。
將一條全部由重邊組成的鏈叫做重鏈。
(圖中加粗的邊為重邊,未加粗的邊為輕邊,圖取自www.baidu.com)
這樣做有什麽用呢?
如上圖剖分後的樹有這樣的性質:
1.每個點都在一條重鏈中(輕子節點所在重鏈鏈頂是它本身)
2.每一條重鏈一定是自上而下(即不會再一條重鏈上出現兩個深度相同的點)
3.任意一個節點到根節點的路徑上最多有log2(n)條輕邊和log2(n)條重鏈。
這樣之後,我們可以按照優先級為“根節點>重子節點>輕子節點”的順序進行兩次dfs,O(n)預處理出dfs序,深度,每個節點所在重鏈的頂端,這樣就能保證每一條重鏈中所有的點的dfs序中的位置都是自上而下連續而遞增的。
然後,我們再用一個線段樹O(log2(n))維護每一條重鏈和輕邊上點的權值的單點修改,區間修改,區間和、區間最值之類的問題。
如果要處理路徑上的問題,我們可以不斷將兩個點中所在重鏈鏈頂深度小的那個點直接跳到它所在重鏈鏈頂的父節點,並對這條重鏈進行操作(如果這個點是輕子節點那麽就直接對這個點進行操作),直到這兩個點到達了同一條重鏈,這是再將兩個點之間的部分進行操作。
這樣,我們每次完成對一條路徑的操作或詢問復雜度為log2(n)乘以 log2(m)(m為每條鏈的平均長度,實際上這個數通常較小)。
這裏附上洛谷模板題和AC代碼。
題目描述
如題,已知一棵包含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
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 #define LL long long
7 #define mid (l+r>>1)
8 #define len (r-l+1)
9 #define M 100100
10 using namespace std;
11 LL read(){
12 LL nm=0,oe=1;char cw=getchar();
13 while(!isdigit(cw)) oe=cw==‘-‘?-oe:oe,cw=getchar();
14 while(isdigit(cw)) nm=nm*10+(cw-‘0‘),cw=getchar();
15 return nm*oe;
16 }
17 LL n,m,f[M],fa[M],nt[M<<1],to[M<<1],tp[M],d[M],w[M];
18 LL cnt,cur,mod,rt,a,b,sz[M],gt[M],ed[M],s[M],num,c[M];
19 LL t[M<<2],mk[M<<2],add,typ;
20 bool fg[M];
21 void link(){nt[++cur]=f[a],f[a]=cur,to[cur]=b;}
22 void dfs1(LL x){
23 sz[x]=1;
24 int sn=0;
25 for(int i=f[x];i!=-1;i=nt[i]){
26 if(to[i]==fa[x]) continue;
27 fa[to[i]]=x,d[to[i]]=d[x]+1;
28 dfs1(to[i]),sz[x]+=sz[to[i]];
29 if(sn==0) sn=i;
30 else if(sz[to[sn]]<sz[to[i]]) sn=i;
31 }
32 if(sn!=0) swap(to[f[x]],to[sn]),fg[to[f[x]]]=true;
33 return;
34 }
35 void dfs2(int x){
36 if(fg[x]) tp[x]=tp[fa[x]];
37 else tp[x]=x;
38 gt[x]=++cnt,s[gt[x]]=x;
39 for(int i=f[x];i!=-1;i=nt[i]){
40 if(to[i]==fa[x]) continue;
41 dfs2(to[i]);
42 }
43 ed[x]=cnt;
44 }
45 int build(int x,int l,int r){
46 if(l==r) return t[x]=w[s[l]];
47 return t[x]=(build(x<<1,l,mid)+build(x<<1|1,mid+1,r))%mod;
48 }
49 void pushdown(int x,int l,int r){
50 mk[x<<1]+=mk[x],t[x<<1]+=(mid-l+1)*mk[x];
51 mk[x<<1|1]+=mk[x],t[x<<1|1]+=(r-mid)*mk[x];
52 mk[x]=0;
53 }
54 void update(int x,int l,int r,int L,int R){
55 if(r<L||l>R) return;
56 if(L<=l&&r<=R){
57 mk[x]+=add;
58 t[x]+=len*add;
59 t[x]%=mod;
60 return;
61 }
62 pushdown(x,l,r);
63 update(x<<1,l,mid,L,R);
64 update(x<<1|1,mid+1,r,L,R);
65 t[x]=(t[x<<1]+t[x<<1|1])%mod;
66 }
67 LL calc(int x,int l,int r,int L,int R){
68 if(r<L||R<l) return 0;
69 if(L<=l&&r<=R) return t[x];
70 pushdown(x,l,r);
71 LL tot=calc(x<<1,l,mid,L,R)+calc(x<<1|1,mid+1,r,L,R);
72 t[x]=(t[x<<1]+t[x<<1|1])%mod;
73 return tot%mod;
74 }
75 void change(){
76 int x=a,y=b;
77 while(tp[x]!=tp[y]){
78 if(d[tp[x]]<d[tp[y]]) swap(x,y);
79 update(1,1,n,gt[tp[x]],gt[x]),x=fa[tp[x]];
80 }
81 if(d[x]>d[y]) swap(x,y);
82 update(1,1,n,gt[x],gt[y]);
83 return;
84 }
85 LL ans(){
86 LL tot=0ll,x=a,y=b;
87 while(tp[x]!=tp[y]){
88 if(d[tp[x]]<d[tp[y]]) swap(x,y);
89 tot+=calc(1,1,n,gt[tp[x]],gt[x]),x=fa[tp[x]];
90 tot%=mod;
91 }
92 if(d[x]>d[y]) swap(x,y);
93 tot+=calc(1,1,n,gt[x],gt[y]);
94 return tot;
95 }
96 int main(){
97 n=read(),m=read(),rt=read(),mod=read();
98 for(int i=1;i<=n;i++) w[i]=read(),f[i]=-1,fg[i]=false;
99 for(int i=1;i<n;i++){
100 a=read(),b=read();
101 link(),swap(a,b),link();
102 }
103 tp[rt]=fa[rt]=rt,d[rt]=1;
104 dfs1(rt),dfs2(rt),build(1,1,n);
105 while(m--){
106 typ=read(),a=read();
107 if(typ==1) b=read(),add=read(),change();
108 else if(typ==2) b=read(),printf("%lld\n",ans()%mod);
109 else if(typ==3) add=read(),update(1,1,n,gt[a],ed[a]);
110 else printf("%lld\n",calc(1,1,n,gt[a],ed[a])%mod);
111 }
112 return 0;
113 }
本人代碼風格較為奇怪,請大家見諒。
洛谷 P3384 【模板】樹鏈剖分