BB
dsu on tree 树上并查集?
其实这东西跟并查集一点关系都没有吧(可能是我太年轻
树上启发式合并 和莫队一样有着 看上去 貌似 特别 高深 的名字,其实就是XJB暴力
正题
实质上dsu on tree运用了一个轻重链剖分的思想。
适用于不带修改的树上询问操作 离线操作 比莫队优越
有些树上题目我们每次暴力时间复杂度是 O ( n 2 ) \mathcal{O(n^2)} O(n2)的,而dsu on tree的复杂度是 O ( n l o g n ) \mathcal{O(nlogn)} O(nlogn)的
对于每个树上每个节点
它的答案是从它的子节点更新上来的 但是有因为我们的空间不允许 所以不能把所有点的所有信息保存下来
但是我们能发现在同一个节点下的子节点是互不干涉的
所以就可以共用了啊
但是每次都要保留一个最重的儿子来减少操作次数
我们先统计一个节点所有的轻儿子 然后删除它的答案
再统计这个节点的重儿子 保留他的答案
最后为了上传信息 再算一遍所有轻儿子 加到答案中就可以了
∵ T ( n ) = 2 T ( n / 2 ) + O ( n ) \because T(n)=2T(n/2)+O(n) ∵T(n)=2T(n/2)+O(n)
∴ T ( n ) = O ( n l o g n ) \therefore T(n)=O(nlogn) ∴T(n)=O(nlogn)
(这应该学过一点数学的都会叭)
例题(dsu on tree 入门经典题)
Codeforces600E. Lomsat gelral
就是维护当前节点下的最多的颜色,就是模板了叭
#include
#include
#include
#define N 100010
using namespace std;
typedef long long LL;
struct Node{
int to, nxt;
}e[N << 1];
LL sum, ans[N];
int size[N], ss[N], lst[N], cnt, tot[N], c[N], maxx, skip[N];
inline void add(int u, int v) {
e[++cnt].to = v;
e[cnt].nxt = lst[u];
lst[u] = cnt;
}
inline void dfs(int x, int fa) {
size[x] = 1;
for (int i = lst[x]; i; i = e[i].nxt) {
int son = e[i].to;
if (son == fa) continue;
dfs(son, x);
size[x] += size[son];
if (!ss[x] || size[son] > size[ss[x]]) ss[x] = son;
}
}
inline void count(int x, int fa, int k) {
tot[c[x]] += k;
if (k > 0 && tot[c[x]] > maxx) {
maxx = tot[c[x]];
sum = c[x];
}
else if (k > 0 && tot[c[x]] == maxx) sum += c[x];
for (int i = lst[x]; i; i = e[i].nxt) {
int son = e[i].to;
if (son == fa || skip[son]) continue;
count(son, x, k);
}
}
inline void makeans(int x, int fa, int kep) {
for (int i = lst[x]; i; i = e[i].nxt) {
int son = e[i].to;
if (son == fa || son == ss[x]) continue;
makeans(son, x, 0);
}
if (ss[x]) makeans(ss[x], x, 1), skip[ss[x]] = 1;
count(x, fa, 1);
ans[x] = sum;
if (ss[x]) skip[ss[x]] = 0;
if (!kep) count(x, fa, -1), maxx = sum = 0;
}
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &c[i]);
}
for (int i = 1, x, y; i < n; ++i) {
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
}
dfs(1, 1);
makeans(1, 1, 1);
for (int i = 1; i < n; ++i) {
cout << ans[i] << " ";
}
cout << ans[n];
return 0;
}