樹鏈剖分學習筆記(二)

2022-09-14 11:45:08 字數 1842 閱讀 7712

這篇是長鏈剖分

並沒有仔細研究過這方面的內容,所以就隨便寫點簡單的東西了

長鏈剖分也是一種樹鏈剖分,所以和輕重鏈剖分很相似

區別是長鏈剖分選擇子樹深度最大的兒子作為重兒子,而不是子樹大小最大的

它也具有一些性質:

鏈長總和是 \(o(n)\) 級別的

好像是句廢話

任意節點 \(x\) 的 \(k\) 級祖先 \(y\) 所在的長鏈的長度大於等於 \(k\)

\(y\) 到 \(x\) 這條鏈的長度為 \(k\) ,而顯然 \(y\) 所在的長鏈不可能比它短

任意節點 \(x\) 到根節點的路徑最多包含 \(o(\sqrt n)\) 條長鏈

從 \(x\) 向上跳,若到達新的長鏈上,則該鏈的長度必然大於之前那條鏈的長度

所以最壞情況下經過的長鏈的長度依次為 \(1,2,3,\cdots\)

鏈長總和為 \(o(n)\) 級別,故鏈的條數為 \(o(\sqrt n)\) 級別

這裡只講一道例題 因為只做了一道

[vijos]lxhgww的奇思妙想

令 \(h=(k)\) 表示 \(k\) 的最高二進位制位,則 \(h>k/2\)

倍增預處理所有 \(2^a\) 級祖先,則回答詢問時我們可以先直接跳到 \(x\) 的 \(h\) 級祖先 \(z\)

然後 \(z\) 所在的長鏈長度大於等於 \(h\)

那麼 \(k\) 級祖先應該就在這條鏈或者其上端的那條長鏈上

憑直覺感受的話這東西應該能 \(o(1)\) 求了

設 \(z\) 所在長鏈長度為 \(len\) ,頂端節點為 \(top\)

從 \(z\) 開始還需向上跳 \(k-h\) 次

故 \(y\) 與 \(top\) 的距離為 \((dep_z-dep_)-(k-h)\)

距離為正則 \(y\) 是 \(top\) 的兒子,為負則 \(y\) 是 \(top\) 的祖先

對於每個 \(top\) ,向上預處理前 \(len\) 級祖先,向下處理前 \(len\) 級兒子

於是就實現了 \(o(1)\) 查詢

而這一步預處理的時間複雜度為 \(o(\sum len)\) ,也就是 \(o(n)\)

總時間複雜度為 \(o(n\log n+m)\)

可以發現倍增預處理是複雜度瓶頸,故只有像本題這樣詢問量很大時長鏈剖分才具有優勢

#include#include#include#define gc (l==r&&(r=(l=c)+fread(c,1,1<<21,stdin),l==r)?eof:*l++)

const int n=300010; int n,m,lgn,cnt,x,y,k,len,t,ans;

int last[n],dep[n],depm[n],top[n],son[n],hb[n];

struct node e[n<<1];

std::vectora[n],b[n]; int sa[n],sb[n],f[n][20];

void dfs(int x,int fa)

}void dfs2(int x,int tx)

int p=-1,p2,num[9];

char c[1<<21],*l=c,*r=c,cw[1<<21];

int read()

inline void flush()

void write(int x)

int main()

else ans=x;

write(ans);

}return flush(),0;

}

樹鏈剖分學習筆記

寫 又犯了很sb的錯誤,線段樹寫錯了。好像每次都會把r l 1寫成l r 1,然後就只有20分。寫的比較醜,壓了壓之後190行。基本上是我打過的最長的乙個模板了 然後簡單介紹一下樹剖吧。樹鏈剖分,就是把樹剖分成鏈,然後用資料結構來維護這些鏈,使得詢問 修改的複雜度達到o logn o l ogn 不...

樹鏈剖分學習筆記

樹鏈剖分 mod estc oder modestcoder modest code r如果你是重兒子,你就在重路徑上。如果你是輕兒子,暴力沿著祖先向上爬最多log nlogn logn 次就可以遇到重路徑。或者到根 而樹上操作基本就是找祖先 也許有人喜歡我的碼風 include include d...

樹鏈剖分學習筆記

前言 書上只講了重鏈剖分,菜雞也只會這一種,想看其他的是別想了。要會樹鏈剖分,首先你需要了解一些概念。我們把乙個節點的所有兒子節點中子樹節點數最大的稱為重兒子,也就是size最大的子節點。size的定義我在講換根dp時說過,因此不再贅述。對於每個節點的重兒子,我們用 son x 來記錄它,父親節點到...