1 条题解
-
0
自动搬运
来自洛谷,原作者为

奇米
**搬运于
2025-08-24 22:17:50,当前版本为作者最后更新于2020-04-18 20:55:19,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题解 -
$$\huge\color{blue}\boxed{\color{Violet}\mathfrak{There\ is \ my \ blog}}$$
题目意思
- P6142
- 给定一棵含有个结点的树,把它分成若干条链(边只能选一次,点可以选多次),使得最短的那条链的长度最长是多少。
- 一道良(凉)好练习题。
- 我们首先肯定会去二分答案那个最长链长,关键是如何判定。
- 我们这边利用这个东东来维护,它是什么讷?就是一个容器,然后加入元素会帮你从小到大排序,且允许加入重复的元素,且删增操作是的。
- 然后我们就有考虑如何去判定:对于一个节点有若干条从连过来的边,我们尽量找道两条链连接使得尽量接近且大于等于。这个还是简单易懂的。于是我们是两两配对所以要从儿子节点连过来并且要有一条边给自己的父亲节点,那么如果从儿子那儿有奇数条边那么就不管了,如果偶数条边,我们强制构造出奇数条边(即加入一条边)。
- 于是我们做完了这道题目,似乎还可以双指针优化到,这儿不多加介绍(因为比较菜。
#include <bits/stdc++.h> #define pb push_back using namespace std; inline int read() { int sum=0,ff=1; char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') ff=-1; ch=getchar(); } while(isdigit(ch)) sum=sum*10+(ch^48),ch=getchar(); return sum*ff; } const int N=1e5+5; int n,m,ans,flg,jb,f[N]; vector<int> G[N]; inline void dfs(int u,int fa,int L) { multiset<int> M;//multiset容器 if(flg) return; for ( int i=0;i<G[u].size();i++ ) { int v=G[u][i]; if(v==fa) continue; dfs(v,u,L); M.insert(f[v]+1); } jb=0; if((u==1&&M.size()&1)||(u!=1&&!(M.size()&1))) M.insert(0); //强制除根节点外的点从儿子连边过来的边数为奇数条 while(M.size()) { multiset<int>::iterator It=M.begin();//取当前最小边 int small=*It; M.erase(It); multiset<int>::iterator Big=M.lower_bound(L-small); //二分找到第一条大于等于二分的L的边 if(u==1) { if(Big==M.end())//找不到,走人 { flg=1; break; } M.erase(Big); } else { if(Big==M.end()&&jb) { flg=1; break; } if(Big==M.end()&&!jb) jb=1,f[u]=small;//找到那一条可以给f[u]造成贡献的边 if(Big!=M.end()) M.erase(Big); } } } inline bool check(int mid) { flg=0; memset(f,0,sizeof f); dfs(1,0,mid); return (!flg); } int main() { n=read(); for ( int i=1;i<n;i++ ) { int x,y; x=read(),y=read(); G[x].pb(y); G[y].pb(x); } int l=0,r=n; while(l<=r) { int mid=(l+r)/2; if(check(mid)) l=mid+1,ans=mid; else r=mid-1;//二分答案 } printf("%d\n",ans); return 0; }
- 1
信息
- ID
- 5191
- 时间
- 2000ms
- 内存
- 250MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者