P3554 [POI2013]LUK-Triumphal arch

P3554 POI2013LUK-Triumphal arch

刚开始以为直接统计一个点最多的子树就好了。
然后发现不是这样的,在涂上面的时候,有多余的可以直接涂到下面去,那么就直接二分+树DP就完了。
也就是说我可以预判你要走哪些。
(f_n) 是还需要多少次才能涂完一个点的全部子树。那么 (f_1 = 0) 就是满足要求的。
(为什么能每一个点转移一次,是因为国王一次走一个点,他每走一个点,我就可以多涂 (mid) 次)
式子是

[f_n = max(0, sum - mid +d_n) ]

(d_n) 是一个点的儿子数目。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

typedef long long ll;
const ll MAXN = 1e6+10;

struct edge {
    ll nt, to, v;
} E[MAXN];

ll N, M, head[MAXN], cnt = -1, ans, d[MAXN], f[MAXN], mid;

void add(ll, ll);
void pre(ll, ll);
bool check();
void dfs(ll, ll);

int main() {
    memset(head, -1, sizeof(head));
    scanf("%lld", &N);
    for (ll x, y, i = 1; i < N; i++) {
        scanf("%lld%lld", &x, &y);
        add(x, y);
        add(y, x);
    }
    pre(1, 0);
    ll l = 0, r = N;
    while (l <= r) {
        mid = l + ((r - l) >> 1);
        if (check()) {
            ans = mid;
            r = mid - 1;
        } else {
            l = mid + 1;
        }
    }
    printf("%lld
", ans);
    return 0;
}

void pre(ll n, ll ff) {
    for (ll i = head[n]; ~i; i = E[i].nt) {
        ll v = E[i].to;
        if (v == ff) continue;
        else {
            pre(v, n);
            d[n]++;
        }
    }
}

bool check() {
    dfs(1, 0);
    return f[1] == 0;
}

void dfs(ll n, ll ff) {
    ll sum = 0;
    for (ll i = head[n]; ~i; i = E[i].nt) {
        ll v = E[i].to;
        if (v == ff) continue;
        else {
            dfs(v, n);
            sum += f[v];
        }
    }
    f[n] = max(0LL, sum - mid + d[n]);
}

void add(ll x, ll y) {
    cnt++;
    E[cnt].to = y;
    E[cnt].nt = head[x];
    head[x] = cnt;
}
Yukkuri si te yi te ne
原文地址:https://www.cnblogs.com/Gensokyo-Alice/p/13674355.html