[学习笔记]dsu on tree

版权声明:蒟蒻的博文,dalao转载标明出处就好吖 https://blog.csdn.net/jokingcoder/article/details/89092819

dsu on tree

BB

dsu on tree 树上并查集?
其实这东西跟并查集一点关系都没有吧(可能是我太年轻
树上启发式合并 和莫队一样有着 看上去 貌似 特别 高深 的名字,其实就是XJB暴力

正题
实质上dsu on tree运用了一个轻重链剖分的思想。
适用于不带修改的树上询问操作 离线操作 比莫队优越

有些树上题目我们每次暴力时间复杂度是 O ( n 2 ) \mathcal{O(n^2)} 的,而dsu on tree的复杂度是 O ( n l o g n ) \mathcal{O(nlogn)}

对于每个树上每个节点
它的答案是从它的子节点更新上来的 但是有因为我们的空间不允许 所以不能把所有点的所有信息保存下来
但是我们能发现在同一个节点下的子节点是互不干涉的
所以就可以共用了啊
但是每次都要保留一个最重的儿子来减少操作次数
我们先统计一个节点所有的轻儿子 然后删除它的答案
再统计这个节点的重儿子 保留他的答案
最后为了上传信息 再算一遍所有轻儿子 加到答案中就可以了

T ( n ) = 2 T ( n / 2 ) + O ( n ) \because T(n)=2T(n/2)+O(n)
T ( n ) = O ( n l o g n ) \therefore T(n)=O(nlogn)
(这应该学过一点数学的都会叭)
例题(dsu on tree 入门经典题)
Codeforces600E. Lomsat gelral
就是维护当前节点下的最多的颜色,就是模板了叭

#include <cstdio>
#include <cstring>
#include <iostream>
#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;
}

猜你喜欢

转载自blog.csdn.net/jokingcoder/article/details/89092819