模板:树上启发式合并(dsu on tree)
生活随笔
收集整理的這篇文章主要介紹了
模板:树上启发式合并(dsu on tree)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 解析
- step1:樹剖
- step2:求出輕兒子的答案(不繼承)
- step3:求出重兒子的答案(繼承)
- step4:加入自己的答案、合并輕兒子的答案并記錄答案
- step5:清空
- 復雜度分析
- 代碼
所謂樹上啟發式合并,就是在樹上進行啟發式合并
(逃)
解析
通過很妙的操作將暴力的復雜度優化到log級別
可以解決對子樹的一些離線靜態問題
大概講一下流程:
step1:樹剖
先剖一下確定重兒子為后來的dsu做準備
順便求出size和dfs序等信息
注意這個dfs序不一定要先搜重兒子
所以直接在一個dfs函數里解決就可以
step2:求出輕兒子的答案(不繼承)
接下來的操作都在dsu的主函數中:
void dfs(int x,int f,bool kep)kep表示當前結點的信息是否需要傳給父親
先遞歸把所有輕兒子的答案求出來
不繼承給父親
step3:求出重兒子的答案(繼承)
求出重兒子的答案,并繼承到父親接著用
if(hson[x]) dfs(hson[x],x,1);step4:加入自己的答案、合并輕兒子的答案并記錄答案
把自己的答案加進來
并利用求好的dfs序
暴力把所有輕兒子的答案合并到父親
最后記錄一下當前的答案
step5:清空
如果不需要傳給父親,就把整個子樹的信息暴力清空
if(!kep){for(int i=L[x];i<=R[x];i++) del(dfn[i]);res=mx==0;}復雜度分析
考慮一個結點被遍歷到幾次
注意到一個結點每作為一個輕兒子的子樹,就會被多遍歷一次
上面那個東西其實就是到根的輕鏈的數目
眾所周知這玩意是log的
所以復雜度是非常優秀的nlogn
應該和樹剖一樣跑的很不滿
當然如果單次合并答案復雜度不是O1那還要再乘合并復雜度
代碼
題目傳送門
#include<bits/stdc++.h> using namespace std; #define ll long long const int N=1e5+100; const int mod=998244353; inline ll read() {ll x=0,f=1;char c=getchar();while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f; } int n,m; struct node{int to,nxt; }p[N<<1]; int fi[N],cnt; inline void addline(int x,int y){p[++cnt]=(node){y,fi[x]};fi[x]=cnt;return; } int siz[N],L[N],R[N],tim,hson[N],dfn[N]; int col[N],bac[N],mx; ll ans[N],res; void dfs0(int x,int f){siz[x]=1;L[x]=++tim;dfn[tim]=x;for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==f) continue;dfs0(to,x);if(siz[hson[x]]<siz[to]) hson[x]=to;siz[x]+=siz[to];}R[x]=tim; } inline void add(int p){int o=col[p];++bac[o];if(bac[o]>mx){mx=bac[o];res=o;}else if(bac[o]==mx) res+=o;return; }void dfs(int x,int f,bool kep){for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==f||to==hson[x]) continue;dfs(to,x,0);}if(hson[x]) dfs(hson[x],x,1);add(x);for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==f||to==hson[x]) continue;for(int j=L[to];j<=R[to];j++) add(dfn[j]);}ans[x]=res;if(!kep){for(int i=L[x];i<=R[x];i++) bac[col[dfn[i]]]=0;res=mx=0;}return; } int main(){#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);#endifmemset(fi,-1,sizeof(fi));cnt=-1;n=read();for(int i=1;i<=n;i++) col[i]=read();for(int i=1;i<n;i++){int x=read(),y=read();addline(x,y);addline(y,x);}dfs0(1,0);dfs(1,0,1);for(int i=1;i<=n;i++) printf("%lld ",ans[i]);return 0; }總結
以上是生活随笔為你收集整理的模板:树上启发式合并(dsu on tree)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 正青春凌潇潇是谁演的 凌潇潇扮演者个人资
- 下一篇: 洛谷P1074:靶形数独(搜索、剪枝)