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;
}
希望我们都有一个光明的未来