长链剖分(知识点整理+板子总结)
思路來(lái)源
https://blog.nowcoder.net/n/5eaebd22f5f846838c637bc337cc1ee9
https://blog.csdn.net/litble/article/details/87965999
知識(shí)點(diǎn)整理
長(zhǎng)鏈剖分,用于維護(hù)子樹(shù)中只與深度有關(guān)的信息,多用于靜態(tài)
從任何一個(gè)點(diǎn)往上跳到根,最多經(jīng)過(guò)條不同的長(zhǎng)鏈。
?
長(zhǎng)鏈剖分,顧名思義,每個(gè)點(diǎn)維護(hù)子樹(shù)中最長(zhǎng)的那一條鏈,
[l[x],l[x]+len[x]-1]是其長(zhǎng)鏈區(qū)間,到l[x]的偏移量也確定了它的深度
和dsu on tree類似,但若干條鏈分別占用不同位置的數(shù)組空間,只有相同鏈的內(nèi)存共用,
所以不用像dsu on tree那樣清空內(nèi)存,所以預(yù)處理長(zhǎng)鏈長(zhǎng)度之后,只需要三步
?
①搜長(zhǎng)兒子,繼承長(zhǎng)兒子的答案
②把短兒子往長(zhǎng)兒子上掛,計(jì)算短兒子的答案
③把自己這個(gè)節(jié)點(diǎn)掛上去
?
由于若干條長(zhǎng)鏈不交,每個(gè)點(diǎn)在向上合并的時(shí)候,只會(huì)作為短鏈出現(xiàn)在一條鏈里,所以是O(n)的
似乎還是指針版的略快一點(diǎn),然而習(xí)慣用數(shù)組下標(biāo)寫……
題目
n(n<=1e6)個(gè)點(diǎn)的樹(shù),
對(duì)于每個(gè)點(diǎn),求其子樹(shù)中出現(xiàn)節(jié)點(diǎn)最多的深度d,
如果存在多個(gè)深度,它們的節(jié)點(diǎn)個(gè)數(shù)相同,則返回最小的哪個(gè)
題解
搞個(gè)裸題,長(zhǎng)鏈剖分合并,
首先繼承長(zhǎng)鏈答案,然后短鏈合并的時(shí)候,如果可更新則更新
最后check一下這棵子樹(shù)的根的深度,判斷其是否能更新答案
代碼
#include<bits/stdc++.h> using namespace std; #define pb push_back const int N=1e6+10; int son[N],len[N];//長(zhǎng)兒子 int n,u,v; int l[N],r[N],dfn;//長(zhǎng)鏈的dfs序區(qū)間段 int sum[N],ans[N]; vector<int>e[N]; void dfs1(int u,int fa){for(int v:e[u]){if(v==fa)continue;dfs1(v,u);if(!son[u] || len[son[u]]<len[v]){son[u]=v;}}len[u]=len[son[u]]+1; } void dfs2(int u,int fa){l[u]=++dfn;r[u]=l[u]+len[u]-1;if(son[u]){dfs2(son[u],u);ans[u]=ans[son[u]]+1;//答案來(lái)自長(zhǎng)兒子 }for(int v:e[u]){if(v==fa || v==son[u])continue;dfs2(v,u);//答案來(lái)自短兒子 for(int j=l[v],k=1;j<=r[v];++j,++k){sum[l[u]+k]+=sum[j];if((k>ans[u] && sum[l[u]+k]>sum[l[u]+ans[u]]) || (k<ans[u] && sum[l[u]+k]>=sum[l[u]+ans[u]])){ans[u]=k;}}}sum[l[u]]++;if(sum[l[u]]>=sum[l[u]+ans[u]]){ans[u]=0;} } int main(){scanf("%d",&n);for(int i=2;i<=n;++i){scanf("%d%d",&u,&v);e[u].pb(v);e[v].pb(u);}dfs1(1,0);dfs2(1,0);for(int i=1;i<=n;++i){printf("%d\n",ans[i]);}return 0; }?
總結(jié)
以上是生活随笔為你收集整理的长链剖分(知识点整理+板子总结)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 我们应该如何提升精力?
- 下一篇: mcc460_最新MCC和MNC国家代码