題目:aragorn's story
題意:給一棵樹,每個結點都有初始的權值,有m個操作,分兩種:一是從x 結點到y 結點路上所有的結點權值+z或-z,二是問x結點的權值。
思路:
樹鏈剖分。
這是我學樹剖的第一題,建議還沒接觸過的夥伴,第一次學習的時候不要一直糾結理論,直接找一道模板題,然後找一篇ac**,直接理解,做完一題後,你就會發現理論其實也挺好理解的,樹剖也挺好學的。
樹剖中的新概念:重孩子、輕孩子、重鏈、輕鏈,後面解釋
fa[x]:x 結點的父結點
dep[x]:x 結點的深度
siz[x]:以x 結點為根的子樹的結點個數
son[x]:x 結點的重孩子,即x 所有孩子中siz 最大的那個(相對的,其他的為輕孩子)
結點x 和他重孩子的連邊叫作重邊,重邊組成的一條鏈叫作重鏈。
top[x]:x 所屬的重鏈的頭結點
xd[x]:和pos相反,表示線段樹中x 位置的結點是哪個
上面的陣列定義給出後,有過基礎的完全可以自己求出來了(下面ac**的dfs1、dfs2),至於求出來什麼用,再看下面。
比如現在要求:x 結點到y 結點路上所有結點的權值+1。
分兩種:1. 如果x、y 兩結點在同一條重鏈上,那麼他們路上的結點其實就是線段樹上連續的乙個區間[x, y],那麼像普通的線段樹區間更新似的便可以解決。2. 如果x、y 不在同乙個重鏈上,找到他們的鏈頭,也就是top[x]、top[y],判斷哪個深度大,選擇深度大的那個,假設為x,現在我們可以更新區間[top[x],x],然後x指向top[x]的父結點,再次判斷x、y是否同一重鏈。
單點查詢就不說了,和線段樹單點查詢一樣。
實現弄完了,我們來研究一下為什麼他可以快速解決該類問題,從區間更新那裡,我們可以看到,如果屬於同一條重鏈,那麼接下來的更新操作是線段樹操作,這個時間複雜度是logn大家都學過了。也就是說如果有可能慢,那就慢在屬於不同的重鏈,而且慢在必須一直跳(也就是說始終跳不到同一條重鏈),慢在重鏈的長度很短(一次只能跳一點)。如果始終沒跳到一條重鏈上,那麼跳的次數最多就是樹的高度,那麼會不會樹的高度很大而一次跳很短呢,答案是否定的,因為結點x 的重孩子是其所有孩子結點中siz 最大的那個,如果要跳的y 在重孩子那棵子樹,那麼邊x-son[x]是重邊,是可以跳過的,如果要跳的y 在輕孩子z那棵子樹,雖然x-z不是重邊,是不能跳過去的,但重孩子至少分去了一半的結點,這樣層層計算下來,最終時間複雜度也是logn,並不會出現跳的次數過多的情況。
ac**:
1 #include2 #include3 #include4using
namespace
std;
5#define n 100010
6#define lson rt<<1
7#define rson rt<<1|1
8int
fa[n],dep[n],siz[n];
9int
son[n];
10 vectore[n];
11void dfs1(int rt,int f,int
h)1224}
25}26int
top[n],pos[n],xd[n],po;
27void dfs2(int rt,int
org)
2839}40
struct
node
4148
};49 node v[n<<2
];50
intnum[n];
5152
void build(int l,int r,int
rt)53
62build(l,v[rt].mid(),lson);
63 build(v[rt].mid()+1
,r,rson);
64 v[rt].w = v[lson].w+v[rson].w;65}
66void update(int val,int l,int r,int
rt)67
74if
(v[rt].c)
7582
int mid=v[rt].mid();
83if(l<=mid) update(val,l,r,lson);
84if(r>mid) update(val,l,r,rson);
85 v[rt].w = v[lson].w+v[rson].w;86}
87void change(int x,int y,int
val)
8895
if(dep[x]>dep[y]) swap(x,y);
96 update(val,pos[x],pos[y],1
);97}98
int query(int rt,int
val)
99110
int mid=v[rt].mid();
111int ret=0
;112
if(val<=mid) ret=query(lson,val);
113else ret=query(rson,val);
114 v[rt].w = v[lson].w+v[rson].w;
115return
ret;
116}
117int
main()
118129
130for(int i=1;i)
131136 dfs1(1,0,0
);137 dfs2(1,1
);138 build(1,n,1
);139
while(m--)
140148
else
149154
}155
}156
return0;
157 }
樹鏈剖分 樹鏈剖分講解
好了,這樣我們就成功解決了對樹上修改查詢邊權或點的問題。下面放上 vector v maxn int size maxn dep maxn val maxn id maxn hson maxn top maxn fa maxn 定義 int edge 1,num 1 struct tree e ma...
數鏈剖分基礎講解
在一棵樹上進行路徑的修改 求極值 求和 乍一看只要線段樹就能輕鬆解決,實際上,僅憑線段樹是不能搞定它的。我們需要用到一種貌似高階的複雜演算法 樹鏈剖分。樹鏈,就是樹上的路徑。剖分,就是把路徑分類為重鏈和輕鏈。重兒子 siz u 為v的子節點中siz值最大的,那麼u就是v的重兒子。輕兒子 v的其它子節...
演算法入門 樹鏈剖分 輕重鏈剖分
目錄 3.0 求 lca 4.0 利用資料結構維護資訊 5.0 例題 參考資料 資料結構入門 線段樹 發表於 2019 11 28 20 39 dfkuaid 摘要 線段樹的基本 建樹 區間查詢 單點修改 及高階操作 區間修改 單點查詢 區間修改 區間查詢 標記下傳 標記永久化 閱讀全文 樹鏈剖分用...