給你一顆有n
n個結點的樹以及m
m個路徑。對於每乙個路徑pat
hipa
thi
,代表著你將會從uiu
i走到vivi
。現在問你,你走完著m
m個路徑後,在這n
n個結點中經過的最多的次數。
首先,如果我們用dfs
dfs在樹上暴力去跑的話,顯然時間肯定是接受不了的。因此我們需要考慮一種較為優美的演算法。
我們發現,經過每乙個路徑pat
hipa
thi
,本質上是使路徑pat
hipa
thi
上的所有的點權+1+
1,如果這個問題是在序列上的話,我們直接就可以用線段樹或差分樹狀陣列去做。而正因為這個問題是在樹上進行的,因此為了簡化問題,我們則需要將樹形的結構轉化成鏈式結構。
因此根據轉化的不同,該問題可以通過樹鏈剖分或者樹上差分去解決。
而顯然樹上差分相對來說比較簡單。
我們只需要令cnt
[ui]
+1cn
t[ui
]+1
,cnt[
vi]+
1cnt
[vi
]+1,並且令cnt
[lca
[ui,
vi]]
−1cn
t[lc
a[ui
,vi
]]−
1,cnt
[fa[
lca[
ui,v
i]]]
−1cn
t[fa
[lca
[ui
,vi
]]]−
1即可。如果我們採用上述差分方法,在最後我們進行dfs
dfs回溯的時候,當前結點u
u就能夠獲得他的兒子們的權值。
整體的時間複雜度:o(n
logn
)o(n
logn
)
#include
using
namespace std;
const
int maxn=
500005
;const
int log=20;
struct nodeq[maxn<<1]
;int head[maxn]
,cnt=0;
void
add_edge
(int from,
int to)
struct lca
}void
init
(int root,
int n)}}
void
swim
(int
&x,int h)
}int
query
(int x,
int y)
}return anc[x][0
];}}lca;
int bit[maxn]
;void
search
(int x,
int fa)
}int
main()
lca.
init(1
,n);
while
(m--
)search(1
,-1)
;int maxx=0;
for(
int i=
1;i<=n;i++
)printf
("%d\n"
,maxx)
;return0;
}
bzoj 3631 (樹上差分)
給你一棵有n n個結點的樹,現在給你乙個大小為n n的排列,說明你的行走路徑。你每經過樹上的每乙個點,你就需要將這個點的點權加1。問你最後所有點的點權大小。根據題目的意思,很明顯這道題是乙個非常典型的點差分的問題。我們只需要對結點uiu i vi vi 以及lca ui,vi l ca u i v ...
樹上差分的整理(點的樹上差分和邊的樹上差分)
點的樹上差分 若經過 u 到 v 的所有點,tmp u tmp v tmp lca u,v tmp parent lca u,v 0 例題 include using namespace std struct ss ss data 600010 int n,q int a 300010 head 6...
BZOJ2588 主席樹 樹上差分
這種類似於第k小的題,一般容易想到主席樹,但是樹鏈並不能不是乙個按順序的序列,使用樹鏈剖分也不太容易維護幾條鏈之間的第k小關係。但是可以從主席樹的字首和思想入手,一般情況的主席樹,查詢的時候是query r query l 1 來詢問區間內的數值數量,在這一題裡面,可以考慮到樹上差分,從樹根開始,以...