看這樣乙個題(dsu on the tree):
給你一棵樹,每個節點有一種顏色,問你每個子樹x的顏色數最多的那種顏色,如果顏色數相同,那麼種類數相加。
考慮最暴力的暴力,對於每個點遍歷它的子樹,統計答案,然後再撤銷。但是這樣太傻了,每個點顯然可以繼承乙個兒子的資訊,我們選擇繼承它的重兒子的資訊,只 dfs 輕兒子。這樣對於每個點,會被 dfs 它到根之間輕邊數量次。所以複雜度是 o(n
logn)
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 級祖先所在長鏈的長度...