图论-树上启发式合并(DSU On Tree)
Posted ghcred
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图论-树上启发式合并(DSU On Tree)相关的知识,希望对你有一定的参考价值。
Disjoint Set Union On Tree ,似乎是来自 Codeforces 的一种新操作,似乎被叫做“树上启发式合并”。
在不带修改的有根树子树信息统计问题中,似乎树上莫队和这个 DSU On Tree 是两类常规操作。
先对树按轻重链剖分。对于每个节点,先计算轻儿子为根的子树信息,每次计算后消除影响,再去计算其他轻儿子。然后计算重儿子为根的子树信息,不消除影响,并把轻儿子们为根的子树信息加入,再合并这个节点本身的信息。由于一个大小 (x) 的子树被消除影响后,都把信息合并到了一个大于等于 (2x+1) 的子树,如此递归下去,它显然至多被消除影响 (log n) 次。利用轻重链剖分的思想,就把这个问题 (O(n)) 解决了(假设合并信息是 (O(1)) 的)。
一道求子树内众数(记在 ans 里)的题的代码:
#include <stdio.h>
#include <vector>
using namespace std;
const int _N = 160000;
vector<int> G[_N];
int hvs[_N], siz[_N], cnt[_N], A[_N], ans[_N], mx;
void connect(int p, int dad)
{
siz[p] = 1;
for (int i = G[p].size() - 1; i >= 0; --i) {
int v = G[p][i];
if (v == dad) continue;
connect(v, p);
siz[p] += siz[v];
if (!hvs[p] || siz[hvs[p]] < siz[v]) hvs[p] = v;
}
return;
}
void clear(int p, int dad)
{
--cnt[A[p]];
for (int i = G[p].size() - 1; i >= 0; --i) {
int v = G[p][i];
if (v == dad) continue;
clear(v, p);
}
return;
}
void insert(int p, int dad)
{
mx = max(mx, ++cnt[A[p]]);
for (int i = G[p].size() - 1; i >= 0; --i) {
int v = G[p][i];
if (v == dad) continue;
insert(v, p);
}
return;
}
void dfs(int p, int dad)
{
for (int i = G[p].size() - 1; i >= 0; --i) {
int v = G[p][i];
if (v == dad || v == hvs[p]) continue;
dfs(v, p);
clear(v, p);
mx = 0;
}
if (hvs[p]) dfs(hvs[p], p);
for (int i = G[p].size() - 1; i >= 0; --i) {
int v = G[p][i];
if (v == dad || v == hvs[p]) continue;
insert(v, p);
}
ans[p] = mx = max(mx, ++cnt[A[p]]);
return;
}
int main()
{
int N;
scanf("%d", &N);
for (int i = 1; i <= N; ++i)
scanf("%d", &A[i]);
for (int a, b, i = 1 ; i < N; ++i) {
scanf("%d%d", &a, &b);
G[a].push_back(b), G[b].push_back(a);
}
connect(1, 0);
dfs(1, 0);
for (int i = 1; i <= N; ++i)
printf("%d ", siz[i] - ans[i]);
return 0;
}
CF 上原博客 [Tutorial] Sack (dsu on tree)
模板题 CF600E 子树众数统计
以上是关于图论-树上启发式合并(DSU On Tree)的主要内容,如果未能解决你的问题,请参考以下文章