資料結構 樹上啟發式合併

2022-07-02 07:06:11 字數 1574 閱讀 8367

這道題也有用樹上莫隊的解法,這裡再給乙個樹上啟發式合併的解法。

樹上啟發式合併,類似輕重鏈剖分,先算出每個節點的重兒子,然後計算答案時先遞迴計算輕兒子的答案,標記clr為true,然後計算重兒子的答案,clr為false,然後把輕兒子的節點暴力插到重兒子裡,最後把父節點自己加入。

int n;

vectorg[maxn];

int siz[maxn], mch[maxn];

void dfs1(int u, int p)

}

由於樹上啟發式合併並不關心深度,所以沒有必要維護深度。

void calc(int u, int p, int skip, int d) 

}void dfs2(int u, int p, bool keep)

if (mch[u])

dfs2(mch[u], u, true);

calc(u, p, mch[u], 1);

for (pii &q : q[u])

if (!keep)

calc(u, p, 0, -1);

}

然後是主要的計算過程dfs2,dfs2優先進入所有的輕兒子,並且不keep輕兒子的答案,保持樹狀陣列為空。然後進入重兒子計算並keep重兒子的結果。這裡使用乙個輔助函式calc,calc的修改值為1時表示向樹狀陣列中新增,然後命令其在新增時skip掉重兒子。計算完畢後樹狀陣列中存著這棵子樹對應的狀態,然後取出所有的詢問進行回答。那之後,假如不keep樹狀陣列,呼叫calc修改值為-1,並且不跳過重兒子,把整棵子樹刪除乾淨。

時間複雜度為 \(o(nlog^2n)\)

這裡的查詢要去重,所以要先計算再查詢。而且要注意cache的命中。一次樹遍歷就統計出所有的資訊,把常用的區域性值放在陣列的低維。

int n, k;

int a[maxn];

vectorg[maxn];

int siz[maxn], mch[maxn];

void dfs1(int u, int p)

}int cnt[1 << 20][17][2];

ll ans;

void calc1(int u, int p, int lca)

for (int &v : g[u])

}void calc2(int u, int p)

for (int &v : g[u])

}void calc3(int u, int p)

}void dfs2(int u, int p, bool keep)

if (mch[u])

dfs2(mch[u], u, true);

int val = a[u];

for (int k = 16; k >= 0; --k)

for (int &v : g[u])

if (!keep)

}}void solve()

dfs1(1, 0);

dfs2(1, 0, true);

printf("%lld\n", ans);

}

樹上啟發式合併

解決樹上統計問題,o n log n o n log n o n lo g n 可以結合線段樹等資料結構維護深度上的資訊 部落格 入門題 const int maxn 1e5 7 const int mod 1e9 7 ll n,m,u,v,mx,sum vector int mp maxn int...

樹上啟發式合併

樹上啟發式合併,一種美妙的黑科技,可以用普通的優化讓你 n 2 變成嚴格 n log 解決一些類似 樹上數顏色,樹上查眾數 這樣的問題 首先你要知道暴力為什麼是 n 2 的 以這個圖為例 每次你從乙個節點開始向下搜,你從1節點搜到3,搜完這個子樹然後你需要把3存的col等資訊刪去再遍歷另乙個子樹才是...

樹上啟發式合併總結

某一天發現一道樹上啟發式合併裸題,但我不會寫 學習並刷了兩天的題,是時候來寫個總結了 樹上啟發式合併 dsu on tree 是乙個在o n logn o nlogn o nlog n 時間內解決許多樹上問題的有力演算法。但它的中心其實是 暴力!沒錯,它正是由暴力優化而來。我們先看一道例題 cf60...