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