P3000 [USACO10DEC]Cow Calisthenics G
Posted wondering-world
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3000 [USACO10DEC]Cow Calisthenics G相关的知识,希望对你有一定的参考价值。
二分 + (dp) 的好题。
首先,要求最大直径最小,我们肯定会想到二分最大直径。 难点就在于怎么 (check)。由于正确性过于显然,二分可行性证明就略了吧。
记录数组 (dp_u) 表示在 (u) 号点,其子树中所有点到达 (u) 号点的最大链长。转移时,考虑其和子树之间的关系。
可以发现,(u) 号点就类似于一个 (lca),两个最长的儿子拼起来的最长链就是这棵子树的最大直径。注意,这里的 (dp_v) 中所存最长链均已保证子树内部合法。
接着考虑子树中直径超过二分的上限怎么办?贪心地想,我们肯定是要断掉最长的那条链,保留次长的。因为这样可以使得我们的总长度尽可能小,从而达到最优次数。
最后,如果必须断边的次数大于上限 (s), 则不合格,应该调大区间,否则一定合格。想一想为什么。(题目中不是要求我们断掉 (s) 条边吗)
具体实现看代码:
#include <bits/stdc++.h>
using namespace std;
#define N 1000010
const int inf = 2e9;
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == ‘-‘) s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ ‘0‘); c = getchar(); }
a = x * s;
return ;
}
struct node{
int v, next;
} t[N << 1];
int f[N];
int bian = 0;
inline void add(int u, int v){
t[++bian] = (node){v, f[u]}, f[u] = bian;
return ;
}
int n, s;
int dp[N];
int sum = 0; // 断了多少次
void dfs(int now, int father, int lim){
int maxn = 0, minn = inf;
for(int i = f[now]; i; i = t[i].next){
int v = t[i].v;
if(v == father) continue;
dfs(v, now, lim);
if(dp[v] + maxn > lim) sum++, maxn = min(maxn, dp[v]); // 保留最小值
else maxn = max(maxn, dp[v]); // 否则更新成最大值
}
dp[now] = maxn + 1;
return ;
}
bool check(int lim){
sum = 0; // 记得清空
dfs(1, 0, lim);
return sum <= s;
}
int main(){
read(n); read(s);
for(int i = 1; i < n; i++){
int x, y;
read(x), read(y);
add(x, y); add(y, x);
}
int l = 1, r = n, ans = 0; // 最多断 n 次
while(l <= r){
int mid = l + r >> 1;
if(check(mid)) ans = mid, r = mid - 1;
else l = mid + 1;
}
cout << ans << endl;
return 0;
}
以上是关于P3000 [USACO10DEC]Cow Calisthenics G的主要内容,如果未能解决你的问题,请参考以下文章
bzoj4745: [Usaco2016 Dec]Cow Checklist
[BZOJ1648][Usaco2006 Dec]Cow Picnic 奶牛野餐
Bzoj3893 [Usaco2014 Dec]Cow Jog
[USACO06DEC]牛的野餐Cow Picnic DFS