關於長鏈剖分

2021-08-27 11:15:57 字數 2308 閱讀 8213

看這樣乙個題(dsu on the tree):

給你一棵樹,每個節點有一種顏色,問你每個子樹x的顏色數最多的那種顏色,如果顏色數相同,那麼種類數相加。

考慮最暴力的暴力,對於每個點遍歷它的子樹,統計答案,然後再撤銷。但是這樣太傻了,每個點顯然可以繼承乙個兒子的資訊,我們選擇繼承它的重兒子的資訊,只 dfs 輕兒子。這樣對於每個點,會被 dfs 它到根之間輕邊數量次。所以複雜度是 o(n

log⁡n)

o(n\log n)

o(nlogn)

。如果需要維護的是關於深度的資訊呢?我們引入長鏈剖分。長鏈剖分,類似於重鏈剖分,我們定義每個點的 len 為從這個點出發向下的最長鏈的長度,把每個點的「長兒子」定義為所有兒子裡 len 最長的點。

考慮維護深度資訊,發現這時候我們繼承重兒子顯得很浪費,我們選擇繼承長兒子,然後合併短兒子的深度。發現每條長鏈只會在鏈頂被遍歷一遍,而長鏈互不相交,因此複雜度是優秀的 o(n

)o(n)

o(n)

。長鏈剖分還有乙個應用是 o(1

)o(1)

o(1)

求 kk

k 級祖先,在這裡就不囉嗦了。

例題:[wc2010]重建計畫

二分答案以後,就是找邊數在 [l,

u]

[l,u]

[l,u

] 的最長鏈。考慮暴力的 dp,設 f[i

][j]

f[i][j]

f[i][j

] 表示 i

ii 的子樹中深度為 j

jj 的點與 i

ii 的最長距離。這是以深度為下標的資訊,我們嘗試用長鏈剖分去優化。一條鏈的 dfs 序是連續的一段,用 f[d

fni+

j]

f[dfn_i+j]

f[dfni

​+j]

表示 f[i

][j]

f[i][j]

f[i][j

],我們發現繼承長兒子資訊的時候深度恰好「後移」了一位。我們用線段樹維護這個陣列,然後合併短兒子的時候順便統計答案。

#include#include#include#includeusing namespace std;

struct edgeed[2000010];

int sz,head[1000010],pos[1000010],len[1000010],son[1000010],tim,w[1000010],n,l,u;

double max[4000010],x,ad[4000010];

double z,f[1000010],g[1000010];

void add_edge(int from,int to,int w)

void push_down(int root,int nl,int nr)

}void update(int root,int l,int r,int x,double k)

int mid=l+r>>1;

push_down(root,mid-l+1,r-mid);

if(x<=mid) update(root<<1,l,mid,x,k);

else update(root<<1|1,mid+1,r,x,k);

max[root]=max(max[root<<1],max[root<<1|1]);

}double query(int root,int l,int r,int x,int y)

void add(int root,int l,int r,int x,int y,double k)

int mid=l+r>>1;

if(x<=mid) add(root<<1,l,mid,x,y,k);

if(y>mid) add(root<<1|1,mid+1,r,x,y,k);

max[root]=max(max[root<<1],max[root<<1|1]);

}void dfs2(int u,int ff)

for(int i=head[u];i;i=ed[i].next)

}for(int j=1;j<=len[v];j++)}}

if(len[u]-1>=l) z=max(z,query(1,1,n,pu+l,pu+min(u,len[u]-1)));

}void clear(int root,int l,int r)

bool check(double h)

void dfs1(int u,int ff)

len[u]++;

}int main()

printf("%.3lf\n",l);

return 0;

}

長鏈剖分隨想

之前寫了那麼長一篇blog 現在不如寫篇小短文 說一下另一種樹鏈剖分方法 長鏈剖分的事情。它可以比重鏈剖分更快地完成一些東西。樹鏈剖分的原始版本重鏈剖分非常經典,這裡就不從頭介紹了。原本的剖分方法是按照子樹大小剖分,與子樹點數最多的兒子連成鏈,所以叫做重鏈剖分 然後顯然就有乙個點到根的路徑上至多 o...

長鏈剖分隨想

之前寫了那麼長一篇blog 現在不如寫篇小短文 說一下另一種樹鏈剖分方法 長鏈剖分的事情。它可以比重鏈剖分更快地完成一些東西。樹鏈剖分的原始版本重鏈剖分非常經典,這裡就不從頭介紹了。原本的剖分方法是按照子樹大小剖分,與子樹點數最多的兒子連成鏈,所以叫做重鏈剖分 然後顯然就有乙個點到根的路徑上至多 o...

長鏈剖分總結

長鏈剖分和輕重鏈剖分十分相似,都是將一棵樹節點的資訊分成多條鏈的資訊,但是前者是以深度剖分,後者則是以子樹大小來剖分。同時長鏈剖分還借鑑了 dsu on tree 的一些 trick 使得它能十分高效地合併子樹資訊。破天荒地寫了證明 所有鏈長度之和為節點數 證明 任意乙個點 k 級祖先所在長鏈的長度...