BZOJ 2097 [Usaco2010 Dec]Exercise 奶牛健美操 二分

本文介绍了一种在无根树上进行操作的问题解决方法,通过转换为有根树并使用二分查找结合链长排序来优化断边选择,确保得到的多个子树的直径不超过特定限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:给出一张n个点的无根树,边权均为1。现可以选择断m条边,获得 m+1棵树,问断边后所有树的直径的最大值最小是多少。

不难想到二分答案,如何判断是否合法呢

将无根树转成有根树后考虑以x为根的子树内的情况。
子树内若有超过答案的就必须通过断一条边使其小于答案。按链长排序依次考虑最长的两条边,若超过答案则断掉最长的。这样对于每一个子树都不超过限制整个也就不超过限制,每次断掉的都是最长的能保证断掉的边尽量少。
最后判断断掉的边数是否大于m即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100005
using namespace std;
struct Edge {
    int from,to,nxt;
    Edge() {}
    Edge(int _from,int _to,int _nxt):
        from(_from),to(_to),nxt(_nxt) {}
}e[N*2];
int n,m,cnt,lim,tot=-1,fir[N],f[N],s[N];
void Add_Edge(int u,int v) {
    e[++tot]=Edge(u,v,fir[u]), fir[u]=tot;
    e[++tot]=Edge(v,u,fir[v]), fir[v]=tot;
    return ;
}
inline bool cmp(const int& lhs,const int& rhs) { return lhs>rhs; }
void dfs(int x,int pa) {
    f[x]=0;
    for(int i=fir[x];~i;i=e[i].nxt) {
        if(e[i].to==pa) continue;
        dfs(e[i].to,x);
    }
    int top=0;
    for(int i=fir[x];~i;i=e[i].nxt)
        if(e[i].to!=pa)
            s[++top]=f[e[i].to]+1;
    if(!top) return ;
    sort(s+1,s+top+1,cmp);
    for(int i=1;i<top;++i)
        if(s[i]+s[i+1]>lim)
            ++cnt, s[i]=0;
    if(s[top]>lim) ++cnt, s[top]=0;
    sort(s+1,s+top+1,cmp);
    f[x]=s[1];
    return ;
}
bool check(int x) {
    lim=x, cnt=0;
    dfs(1,0);
    return cnt<=m;
}
int main() {
    memset(fir,-1,sizeof fir);
    scanf("%d%d",&n,&m);
    for(int i=1,x,y;i<n;++i) scanf("%d%d",&x,&y), Add_Edge(x,y);
    int l=0,r=n,mid,ans;
    while(l<=r) {
        mid=l+r>>1;
        if(check(mid)) ans=mid, r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值