Blog of RuSun

\begin {array}{c} \mathfrak {One Problem Is Difficult} \\\\ \mathfrak {Because You Don't Know} \\\\ \mathfrak {Why It Is Diffucult} \end {array}

Ucup 3-2 Zielona Gora

The 3rd Universal Cup. Stage 2: Zielona Góra

B

搬运哥哥的题解。

考虑固定根的情况,有一个显然的贪心算法:自底向上,每个顶点合并子树的人所在顶点集合,如果该顶点没有人,则将子树内最深的人移至该顶点(实际上是链上的点都往上移一步,但是等价)。

需要支持集合的合并、求最大值、删除最大值。可以使用左偏树维护,可持久化以后自然可以实现换根。换根时不能直接使用顶点的深度了,而是需要在每个点处 $+1$ ,因此需要维护加法标记。

时间复杂度 $O(nlogn)$ 。

查看代码
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <cstdio>
#include <vector>
#define pb push_back
using namespace std;
typedef long long L;
const int N = 2e5 + 10, M = 4e7 + 10;
struct Node
{
int v, ls, rs, d, tag;
} tr[M];
L fi[N], fo[N];
int n, idx, p[N], si[N], so[N];
bool vis[N];
vector <int> e[N];
void add (int &x, int k = 1)
{
if (!x) return;
tr[++idx] = tr[x]; x = idx;
tr[x].v += k, tr[x].tag += k;
}
void pushdown (int x)
{
if (!tr[x].tag) return;
add(tr[x].ls, tr[x].tag);
add(tr[x].rs, tr[x].tag);
tr[x].tag = 0;
}
int merge (int x, int y)
{
if (!x || !y) return x | y;
if (tr[x].v < tr[y].v) swap(x, y);
tr[++idx] = tr[x]; x = idx;
pushdown(x);
tr[x].rs = merge(tr[x].rs, y);
if (tr[tr[x].ls].d < tr[tr[x].rs].d)
swap(tr[x].ls, tr[x].rs);
tr[x].d = tr[tr[x].rs].d + 1;
return x;
}
void pop (int &x)
{
pushdown(x);
x = merge(tr[x].ls, tr[x].rs);
}
void dfs1 (int u = 1)
{
for (int v : e[u]) if (v ^ p[u])
{
p[v] = u;
dfs1(v);
fi[u] += fi[v];
si[u] = merge(si[u], si[v]);
}
add(si[u]);
if (vis[u])
si[u] = merge(si[u], ++idx);
else if (si[u])
{
fi[u] += tr[si[u]].v;
pop(si[u]);
si[u] = merge(si[u], ++idx);
}
}
void dfs2 (int u = 1)
{
int d = e[u].size();
vector <int> pre(d), suf(d);
for (int i = 0; i < d - 1; ++i)
{
int v = e[u][i];
pre[i + 1] = merge(pre[i], v == p[u] ? so[u] : si[v]);
}
for (int i = d - 1; i > 0; --i)
{
int v = e[u][i];
suf[i - 1] = merge(suf[i], v == p[u] ? so[u] : si[v]);
}
L s = 0;
for (int v : e[u])
s += v == p[u] ? fo[u] : fi[v];
for (int i = 0; i < d; ++i)
{
int v = e[u][i];
if (v == p[u]) continue;
fo[v] = s - fi[v];
so[v] = merge(pre[i], suf[i]);
add(so[v]);
if (vis[u]) so[v] = merge(so[v], ++idx);
else if (so[v])
{
fo[v] += tr[so[v]].v;
pop(so[v]);
so[v] = merge(so[v], ++idx);
}
dfs2(v);
}
}
int main ()
{
tr[0].d = -1;
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%1d", &vis[i]);
for (int i = 1, a, b; i < n; ++i)
{
scanf("%d%d", &a, &b);
e[a].pb(b), e[b].pb(a);
}
dfs1(), dfs2();
for (int u = 1; u <= n; ++u)
{
int s = 0;
L res = 0;
for (int v : e[u])
if (v == p[u])
{
s = merge(s, so[u]);
res += fo[u];
}
else
{
s = merge(s, si[v]);
res += fi[v];
}
add(s);
if (!vis[u] && s)
res += tr[s].v;
printf("%lld\n", res);
}
return 0;
}
查看代码
1