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;
}
posted @ 2023-11-20 21:18  5k_sync_closer  阅读(4)  评论(0编辑  收藏  举报  来源