AT_agc034_e [AGC034E] Complete Compress 题解
可以发现若最终所有棋子移动到 $x$,则操作次数一定是初始时所有棋子到 $x$ 的距离和除以二,
于是只需求出哪些 $x$ 可以作为最终所有棋子移动到的位置。
设 $s_i$ 表示 $i$ 子树内初始时棋子个数,$f_i$ 表示 $i$ 子树内棋子经过若干操作后到 $i$ 距离和最小是多少,$g_i$ 表示 $i$ 子树内棋子初始时到 $i$ 距离和,
考虑 $u$ 若有一孩子 $v$ 满足 $f_v+s_v>g_u-g_v-s_v$,则 $v$ 子树内的棋子一定不能全部移动到 $u$ 上,
于是此时的最优策略为其他所有子树的棋子均选择 $v$ 子树内的棋子匹配,
这样其他所有子树的棋子均被移到 $u$ 上,而 $v$ 子树内的棋子到 $u$ 距离和还剩 $f_v+s_v-(g_u-g_v-s_v)$,
也就是 $f_u=f_v+s_v-(g_u-g_v-s_v)$。
如果 $u$ 没有这样的孩子 $v$,那最终至多剩一个棋子与 $u$ 距离为 $1$,也就是 $f_u=g_u\bmod 2$。
以 $x$ 为根跑这个 DP,若得到的 $f_x=0$,则 $x$ 可以作为最终所有棋子移动到的位置。
发现需要以每个点为根的 DP 值,换根 DP 即可。
#include <cstdio>
#include <algorithm>
#define int long long
using namespace std;
inline int R()
{
int q = 0;
char c = getchar();
while (c < '0' || c > '9')
c = getchar();
while (c >= '0' && c <= '9')
q = q * 10 + c - '0', c = getchar();
return q;
}
inline int I()
{
char c = getchar();
while (c < '0' || c > '9')
c = getchar();
return c - '0';
}
struct E
{
int v, t;
} e[2000050];
int n, c, q = 1e18, a[1000050], s[1000050], f[1000050], g[1000050], p[1000050], z[1000050][2], o[1000050][2], h[1000050];
void A(int u, int v)
{
e[++c] = {v, h[u]};
h[u] = c;
}
void D1(int u, int k)
{
s[u] = a[u];
for (int i = h[u], v; i; i = e[i].t)
if ((v = e[i].v) != k)
D1(v, u), p[u] += f[v] + s[v], g[u] += g[v] + s[v], s[u] += s[v];
for (int i = h[u], v; i; i = e[i].t)
if ((v = e[i].v) != k)
{
if (f[v] + s[v] + g[v] + s[v] > o[u][0])
z[u][1] = z[u][0], z[u][0] = v, o[u][1] = o[u][0], o[u][0] = f[v] + s[v] + g[v] + s[v];
else if (f[v] + s[v] + g[v] + s[v] > o[u][1])
z[u][1] = v, o[u][1] = f[v] + s[v] + g[v] + s[v];
}
if (o[u][0] > g[u])
f[u] = o[u][0] - g[u];
else
f[u] = p[u] & 1;
}
void D2(int u, int k)
{
int S = s[u], F = f[u], P = p[u], G = g[u], Z0 = z[u][0], Z1 = z[u][1], O0 = o[u][0], O1 = o[u][1];
for (int i = h[u], v; i; i = e[i].t)
if ((v = e[i].v) != k)
{
s[u] -= s[v];
g[u] -= g[v] + s[v];
p[u] -= f[v] + s[v];
if (z[u][0] == v)
z[u][0] = z[u][1], o[u][0] = o[u][1];
if (o[u][0] > g[u])
f[u] = o[u][0] - g[u];
else
f[u] = p[u] & 1;
s[v] += s[u];
g[v] += g[u] + s[u];
p[v] += f[u] + s[u];
if (f[u] + s[u] + g[u] + s[u] > o[v][0])
z[v][1] = z[v][0], z[v][0] = u, o[v][1] = o[v][0], o[v][0] = f[u] + s[u] + g[u] + s[u];
else if (f[u] + s[u] + g[u] + s[u] > o[v][1])
z[v][1] = u, o[v][1] = f[u] + s[u] + g[u] + s[u];
if (o[v][0] > g[v])
f[v] = o[v][0] - g[v];
else
f[v] = p[v] & 1;
D2(v, u);
s[u] = S, f[u] = F, p[u] = P, g[u] = G, z[u][0] = Z0, z[u][1] = Z1, o[u][0] = O0, o[u][1] = O1;
}
}
signed main()
{
// freopen("charlotte.in", "r", stdin);
// freopen("charlotte.out", "w", stdout);
while (~scanf("%d", &n))
{
for (int i = 1; i <= n; ++i)
a[i] = I();
for (int i = 1, u, v; i < n; ++i)
u = R(), v = R(), A(u, v), A(v, u);
D1(1, 0);
D2(1, 0);
for (int u = 1; u <= n; ++u)
if (!f[u])
q = min(q, g[u] >> 1);
printf("%lld\n", q == 1e18 ? -1 : q);
c = 0;
q = 1e18;
for (int i = 1; i <= n; ++i)
a[i] = s[i] = f[i] = g[i] = p[i] = z[i][0] = z[i][1] = o[i][0] = o[i][1] = h[i] = 0;
}
return 0;
}