P5021赛道修建题解
看到最小值最大,很容易想到二分。
那么我们想想怎么\(check\)
在找路径的时候,显然让路径越接近要\(check\)的\(mid\)就越好。而且我们不能漫无目的的凑,要找个神奇的顺序,所以我们不妨使用\(dfs\)。
在\(dfs\)时,对于每一个节点,考虑它所有连着儿子的边。如果这条边的长度大于\(mid\),那么它本身就可以成为一条路径。反之,则找到另一条边\(j\),使得\(dis[i]+dis[j]>=mid\)且不存在边\(k\)使得\(dis[i]+dis[k]\geqslant mid\)\(\&\&\)\(dis[k]<dis[j]\)。
如果还有剩下的边,那就考虑把这些边中最大的边接到当前节点上。
这些操作可以用\(multiset\)搞定(当然我写丑了要吸氧)
有关\(multiset\)
\(multiset\)就是可以有重复元素的\(set\)。
常用函数:
multiset<int> q;
将x插入进multiset: q.insert(x)
删除所有值为x的元素: q.erase(x)
删除x这个位置的元素(此时x为迭代器): q.erase(x)
删除[l,r)这一段的元素: q.erase(l,r)
第一个元素的位置: q.begin()
最后一个元素的后一个位置: q.end()
最后一个元素的位置: q.rbegin()
第一个元素的前一个位置: q.rend()
寻找值为x的元素的位置: q.find(x)(找不到返回q.end())
找到第一个大于等于x的元素的位置: q.lower_bound(x)(失败返回q.end())
找第一个大于x的元素的位置: q.upper_bound(x)(失败返回q.end())
判空: q.empty()
清空: q.clear()
\(Code\)(要开\(c++11\)以及吸氧)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<deque>
#include<set>
#define pa pair<int,int>
#define rg register
#define ls (k<<1)
#define rs (k<<1|1)
using namespace std;
typedef long long ll;
const double eps=1e-13;
inline int read(){
char ch=getchar();
int x=0;bool f=0;
while(ch<'0'||ch>'9')
{
if(ch=='-') f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return f?-x:x;
}
const int N=50009;
int n,m,head[N],cnt,l=2147483647,r,rtn;
struct E{
int to,nxt,dis;
}ed[N<<1];
inline void add(int fr,int to,int dis){
ed[++cnt].to=to;
ed[cnt].dis=dis;
ed[cnt].nxt=head[fr];
head[fr]=cnt;
}
int dfs(int u,int f,int x){
multiset<int> q;
for(rg int e=head[u];e;e=ed[e].nxt){
int v=ed[e].to;
if(v==f) continue;
int d=ed[e].dis+dfs(v,u,x);
if(d>=x) rtn++;//rtn统计找到的路径条数
else q.insert(d);
}
if(q.empty()) return 0;
int rr=0;
while(!q.empty()){
auto i=q.begin();
int d=x-*i,qwq=*i;
q.erase(i);//注意先删除当前元素,这样才能准确知道是否有和这个元素相等的符合条件的元素
auto j=q.lower_bound(d);
if(j==q.end()) rr=max(rr,qwq);//选择最长的接上去
else{
rtn++;
q.erase(j);
}
}
return rr;//返回接上去的长度
}
inline bool ck(int x){
rtn=0;
int orz=dfs(1,0,x);
if(rtn>=m) return 1;
return 0;
}
int main(){
n=read();m=read();
for(rg int i=1;i<n;i++){
int fr=read(),to=read(),dis=read();
add(fr,to,dis);add(to,fr,dis);
r+=dis;l=min(l,dis);
}
r/=m;//讨论区大佬神奇的优化
while(l<=r){
int mid=(l+r)>>1;
if(ck(mid)) l=mid+1;
else r=mid-1;
}
printf("%d\n",r);
}