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\n", 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;
}
posted @ 2020-09-15 17:43  Gensokyo_Alice  阅读(81)  评论(0编辑  收藏  举报