【題目描述】
lxhgww 在樹上玩耍時,lzx2019 走了過來。lxhgww 突然問道:「我現在的k級祖先是誰?」
lzx2019 答道:「不是我嗎?」。接著 lxhgww 就用教主之力讓 lzx2019 消失了,現在他轉過頭準備向你求助。
【輸入】
第一行包含乙個整數n,表示樹的結點數。
接下來n-1行包含兩個整數x,y,表示第x個結點和第y個結點間有一條邊。
接下來1行包含乙個整數m,表示詢問個數。
接下來m行包含兩個整數a,b,則x=alastans,k=blastans。
【輸出】
輸出包含m行,分別是m個詢問的答案。
若沒有k級祖先,則輸出0
【樣例輸入】
51 2
2 31 4
4 55
5 06 7
4 52 1
4 3【樣例輸出】51
011限制
每個測試點1s,512mb
提示資料範圍:
1 ≤ n ≤300000, m ≤ 1800000
1 ≤ x,k≤ n
這道題應該使用長鏈剖分。
簡單介紹一下長鏈剖分。長鏈剖分和重鏈剖分差不多,只是不維護節點最多的兒子,而維護能夠向下深度最大的兒子。
分兩次dfs,第一次維護每個節點的父節點,向下走的最大深度的兒子,深度,以及向下的最大深度的大小。第二次維護每一條長鏈的鏈節點,以及鏈的長度。
而這樣我們將會有乙個優雅的性質。乙個節點的k級祖先所在鏈的長度一定大於等於k。
那麼我們可以採取以下步驟處理本題:
1.長鏈剖分,同時倍增維護每個節點的2的冪次的祖先。
2.記鏈長度為len,陣列維護每個鏈端點向上len長度的祖先和向下len長度的鏈上節點。
3.對於要詢問的點x,我們可以先跳到x的k的最高位的1 的父親的地方。這樣我們可以保證x到達的節點所在鏈長度一定大於k的最高位的1(由那個優雅的性質可得),也就大於剩下的需要向上走的步數。因此我們只需要在鏈端點判斷一下向上走幾步或者向下走幾步就可以找到k級祖先了。
**:
#include
#include
#include
#include
#include
#include
#define re register
#define ll long long
using
namespace std;
int n,m,a,b,c;
struct nodee[
2000001];
int nxp[
2000001];
int f[
1000001];
int cnt=0;
vector<
int>up[
1000001];
vector<
int>down[
1000001];
inline
void
add(
int u,
int v)
int son[
1000001];
int dep[
1000001];
int top[
1000001];
int mxd[
1000001];
int len[
1000001];
int d[
1000001];
int bin[
3000001];
int fa[
1000001][
25];void
dfs1
(int u,
int ff)
}inline
intred()
void
dfs2
(int u)
for(
int re i=f[u]
;i;i=nxp[i])}
void
pre1()
void
pre2()
}int lans=0;
intfind
(int x,
int k)
intmain()
top[1]
=1;dfs1(1
,0);
dfs2(1
);pre2()
; m=
red();
for(
int re i=
1;i<=m;i++
)}
vijos lxhgww的奇思妙想(長鏈剖分)
傳送門 長鏈剖分的板子 又是亂搞優化暴力 對於每乙個點,我們定義它深度最深的子節點為它的重兒子 為什麼不叫長兒子 他們之間的連邊為重邊 然後長鏈剖分有幾個性質 1.總鏈長為 o n 2.乙個節點的 k 級祖先的子樹深度必定大於等於當前節點的子樹深度 以上兩點稍微yy一下就能發現是對的 然後回到這道題...
奇思妙想的演算法隨筆
有n個數,現在要求兩兩之間差值的和,要求盡量低的複雜度。思路a 直接列舉,o n 2 的複雜度 int ans 0 for i 1 i n i 思路b 我們將整個陣列進行由小到大的排序,然後使用公式法。假設排序完之後的陣列狀態為 此時我們關注按照不同起點分割答案,那麼對於每乙個起點 我們將他以段的形...
長鏈剖分隨想
之前寫了那麼長一篇blog 現在不如寫篇小短文 說一下另一種樹鏈剖分方法 長鏈剖分的事情。它可以比重鏈剖分更快地完成一些東西。樹鏈剖分的原始版本重鏈剖分非常經典,這裡就不從頭介紹了。原本的剖分方法是按照子樹大小剖分,與子樹點數最多的兒子連成鏈,所以叫做重鏈剖分 然後顯然就有乙個點到根的路徑上至多 o...