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