【数据结构 线段树合并】CF600E Lomsat gelral

题意

给出一颗树,求每个子树中最多的颜色的编号的总和。

思路

题目有点拗口,其实就是求众数的和。

利用权值线段树维护区间答案以及众数出现的次数。枚举当前点的每个儿子,把儿子们上线段树的信息合并。

线段树合并的时空复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),所以 1 e 5 1e5 1e5能过掉。

思路

#include <cstdio>
#include <algorithm>
 
struct SegmentTree {
	int l, r, maxx;
	long long ans;
}t[2000001];
int n, tot, cnt;
int a[100001], ver[200001], head[100001], next[200001], root[200001];
long long ans[100001];
 
void add(int u, int v) {
	ver[++tot] = v;
	next[tot] = head[u];
	head[u] = tot;
}
 
void spread(int a) {
	if (t[t[a].l].maxx > t[t[a].r].maxx) {
		t[a].maxx = t[t[a].l].maxx;
		t[a].ans = t[t[a].l].ans;
	} else if (t[t[a].l].maxx < t[t[a].r].maxx) {
		t[a].maxx = t[t[a].r].maxx;
		t[a].ans = t[t[a].r].ans;
	} else {
		t[a].maxx = t[t[a].l].maxx;
		t[a].ans = t[t[a].l].ans + t[t[a].r].ans;
	}
}
 
int merge(int r1, int r2, int l, int r) {
	if (!r1 || !r2) return r1 + r2;
	if (l == r) {
		t[r1].maxx += t[r2].maxx;
		return r1;
	}
	int mid = l + r >> 1;
	t[r1].l = merge(t[r1].l, t[r2].l, l, mid);
	t[r1].r = merge(t[r1].r, t[r2].r, mid + 1, r);
	spread(r1);
	return r1;
}
 
void update(int p, int l, int r, int pos) {
	if (l == r) {
		t[p].maxx++;
		t[p].ans = l;
		return;
	}
	int mid = l + r >> 1;
	if (pos <= mid) {
		if (!t[p].l) t[p].l = ++cnt;
		update(t[p].l, l, mid, pos);
	} else {
		if (!t[p].r) t[p].r = ++cnt;
		update(t[p].r, mid + 1, r, pos);
	}
	spread(p);
}
 
void dfs(int p, int fa) {
	for (int i = head[p]; i; i = next[i]) {
		if (fa == ver[i]) continue;
		dfs(ver[i], p);
		root[p] = merge(root[p], root[ver[i]], 1, n);
	}
	if (!root[p]) root[p] = ++cnt;
	update(root[p], 1, n, a[p]);
	ans[p] = t[root[p]].ans;
}
 
int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		scanf("%d", a + i);
	for (int i = 1, u, v; i < n; i++)
		scanf("%d %d", &u, &v), add(u, v), add(v, u);
	dfs(1, 0);
	for (int i = 1; i <= n; i++)
		printf("%lld ", ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值