「20161224-T2」颜色-点分治

小 $A$ 住在魔法森林里,魔法森林的道路构成了一个 $n$ 个节点的树,在这棵树的每个节点上都生长了蘑菇。蘑菇有若干种不同的颜色。
现在小 $M$ 要到小 $A$ 那里去。小 $M$ 想要炼制一种药剂需要很多不同颜色的蘑菇。
她想要在去小 $A$ 家的路上采摘尽可能多不同颜色的蘑菇。然而,她忘了小 $A$ 家在那里。她假设小 $A$ 家会等概率随机在 $n$ 个点的某一个上。好在,小 $M$ 可以从任意一个节点出发来走到小 $A$ 家。
现在小 $M$ 想要知道,对于每一个节点,假如她从这个节点出发,采摘到蘑菇种数的期望是多少。为了避免不必要的麻烦,请将答案 $*n$ 之后输出。

输入

第一行一个数 $n$,表示树的节点数。
第二行 $n$ 个数 $c_i$ ,表示每个节点的蘑菇的颜色。
接下来 $n-1$ 行每行 $2$ 个数 $u$ 和 $v$,表示魔法森林里的一条道路连接 $u$ 和 $v$。

输出

输出共 $n$ 行,表示从第 $i$ 个节点出发,能采摘到蘑菇的种数的期望 $*n$ 的结果。

样例数据

输入

1
2
3
4
5
6
5
1 2 3 2 3
1 2
1 3
2 4
2 5

输出

1
2
3
4
5
10
9
12
9
11

题解

来自hzq84621
每一种颜色贡献是独立的。所以我们可以对于每一种颜色点分一次,每次统
计从这个节点到重心的路径上有这种颜色的节点有几个。
然后对于每个节点,根据它到重心的路径上是否有这种颜色更新答案。
假如有,就是整个子树的 $size$ 减去重心下它所在子树的 $size$,否则就是整
个子树中满足到重心的路径上有这种颜色的节点数减去它所在子树满足这个条
件的节点数。而它的每一种颜色的贡献好像可以一次性点分的时候分别统计。
注意:此题小心爆栈。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <bits/stdc++.h>
const int IN_LEN = 3000000, OUT_LEN = 400000;
char ibuf[IN_LEN], obuf[OUT_LEN], *ih = ibuf, *oh = obuf;
inline void read(int &x) { for (x = 0; !isdigit(*ih); ih++); while (isdigit(*ih)) x = (x << 1) + (x << 3) + ((*ih++) ^ '0'); }
template<class T>
inline void write(T x) {
static int buf[30], cnt;
if (!x) *oh++ = 48;
else {
for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
while (cnt) *oh++ = buf[cnt--];
}
}
const int MAXN = 300005;
struct Edge {
int to;
Edge *next;
inline void init(const int to, Edge *next) { this->to = to, this->next = next; }
} edge[MAXN << 1], *head[MAXN], *cur = edge;
inline void addEdge(int u, int v) { (++cur)->init(v, head[u]), head[u] = cur; }
int n, croot, csize, S, tmp;
int a[MAXN], size[MAXN];
long long ans[MAXN], tot;
int s[MAXN];
bool vis[MAXN], used[MAXN];
inline void getRoot(int root, int fa) {
register int tmp = 0;
size[root] = 1;
for (Edge *i = head[root]; i; i = i->next)
if ((!vis[i->to]) && (i->to != fa))
getRoot(i->to, root), size[root] += size[i->to], tmp = std::max(tmp, size[i->to]);
if (S - size[root] > tmp) tmp = S - size[root];
if (tmp < csize) croot = root, csize = tmp;
}
inline void dfs1(int root, int fa, int v) {
bool valid = false;
if ((!used[a[root]]) && (a[root] != tmp)) used[a[root]] = valid = true, s[a[root]] += size[root] * v, tot += size[root] * v;
for (Edge *i = head[root]; i; i = i->next) if ((!vis[i->to]) && (i->to != fa)) dfs1(i->to, root, v);
if (valid) used[a[root]] = false;
}
inline void dfs2(int root, int fa, int p) {
bool valid = false;
if ((!used[a[root]]) && (a[root] != tmp)) used[a[root]] = valid = true, tot += p - s[a[root]];
ans[root] += tot;
for (Edge *i = head[root]; i; i = i->next)if ((!vis[i->to]) && (i->to != fa)) dfs2(i->to, root, p);
if (valid) used[a[root]] = false, tot -= p - s[a[root]];
}
inline void solve(int root) {
csize = n, getRoot(root, 0), getRoot(croot, 0), tmp = a[croot];
for (Edge *i = head[croot]; i; i = i->next)if (!vis[i->to]) dfs1(i->to, croot, 1);
tot += size[croot]; ans[croot] += tot;
for (Edge *i = head[croot]; i; i = i->next)
if (!vis[i->to])
dfs1(i->to, croot, -1), tot -= size[i->to], dfs2(i->to, croot, size[croot] - size[i->to]), dfs1(i->to, croot, 1), tot += size[i->to];
for (Edge *i = head[croot]; i; i = i->next) if (!vis[i->to]) dfs1(i->to, croot, -1);
tot = 0, vis[croot] = true;
for (Edge *i = head[croot]; i; i = i->next) if (!vis[i->to]) S = size[i->to], solve(i->to);
}
int main() {
fread(ibuf, 1, IN_LEN, stdin), read(n);
for (register int i = 1; i <= n; i++) read(a[i]);
for (register int i = 1, u, v; i < n; i++) read(u), read(v), addEdge(u, v), addEdge(v, u);
S = n, solve(1);
for (register int i = 1; i <= n; i++) write(ans[i]), *oh++ = '\n';
fwrite(obuf, 1, oh - obuf, stdout);
return 0;
}

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×