CF600E Lomsat gelral

CF600E Lomsat gelral

题目大意

很清楚吧

题解

这是 dsu on tree(中文是啥??)入门题QWQ

首先考虑暴力,很简单

考虑有技巧的暴力

先轻重链剖分

当到树上的一个节点的时候,

先把轻儿子暴力算一遍,把贡献删了

然后再把重儿子算一遍, 不用删贡献,

再把轻儿子的贡献加到重儿子上面就可以了

时间复杂度为什么是对的呢???

对于每个节点,他只可能被它祖先的轻边遍历到,而一个节点到根最多只会经过log条轻边,所以当前节点最多只会被遍历log次

为什么一个节点到根最多只会经过log条轻边
考虑对于每个点,它的轻儿子的大小一定小于其他所有儿子的大小的和,所以节点的数量会除以二

同样,如果当前节点是作为轻儿子,那它往上跳的时候节点数量为至少乘2

所以做多经过log条轻边

然后代码实现就很简单了

#include<bits/stdc++.h>
#define N 100005
using namespace std;
struct edge {
	int v, nxt;
}e[N << 1];
int p[N], eid;
void init() {
	memset(p, -1, sizeof p);
	eid = 0;
}
void insert(int u, int v) {
	e[eid].v = v;
	e[eid].nxt = p[u];
	p[u] = eid ++;
}
int size[N], w[N], cnt[N], col[N], n;
long long MC, CC, ans[N];
void dfs1(int u, int fa) {
	size[u] = 1;
	for(int i = p[u]; i + 1; i = e[i].nxt) {
		int v = e[i].v;
		if(v == fa) continue;
		dfs1(v, u);
		if(size[v] > size[w[u]]) w[u] = v;
		size[u] += size[v];
	}
}
void calc(int u, int fa, int o, int W) {
	cnt[col[u]] += o;
	if(cnt[col[u]] == MC) CC += col[u];
	if(cnt[col[u]] > MC) MC = cnt[col[u]], CC = col[u];
	
	for(int i = p[u]; i + 1; i = e[i].nxt) {
		int v = e[i].v;
		if(v == fa || v == W) continue;
		calc(v, u, o, W);			
	}
}
void dfs2(int u, int fa, int o) {
	for(int i = p[u]; i + 1; i = e[i].nxt) {
		int v = e[i].v;
		if(v == fa || v == w[u]) continue;
		dfs2(v, u, -1);
	}
	if(w[u]) dfs2(w[u], u, 1);
	calc(u, fa, 1, w[u]);
	ans[u] = CC;
	if(o == -1) {
		calc(u, fa, -1, 0);
		MC = 0, CC = 0;
	}
}
int main() {
	init();
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++) scanf("%d", &col[i]);
	for(int i = 1; i < n; i ++) {
		int u, v;
		scanf("%d%d", &u, &v);
		insert(u, v);
		insert(v, u);
	}
	dfs1(1, 0);
	dfs2(1, 0, 1);
	for(int i = 1; i <= n; i ++) printf("%lld ", ans[i]);
	return 0;
}

坑点

long long


感觉没有启发式合并 or 线段树合并好写啊 QWQ

posted @ 2019-11-06 14:58  lahlah  阅读(27)  评论(0编辑  收藏  举报