背景:
一年前自己还是太菜了。
虽然现在也菜。
题目传送门:
https://www.luogu.org/problem/P5021
题意:
一棵树,选择
m
m
m条边铺设,要求这些边不能有交集,且不允许掉头(我们认为它是从一点修到另外一点的),现在求这些赛道的最短值的最大值。
思路:
现在看来还是一个眼题的。
二分答案
m
i
d
mid
mid。
考虑儿子的信息通过当前的边来合并。
若当前已经可以更新到
m
i
d
mid
mid,直接方案数加
1
1
1即可。
若当前不能更新到
m
i
d
mid
mid,考虑从小到大选出尽可能小的能与它配对达到
m
i
d
mid
mid的,将这两个配对在一起(相当与这两条路径通过当前点连在一起),方案数加
1
1
1。
最后传上去最大的即可。
可用
multiset
\text{multiset}
multiset维护即可。
注意
multiset
\text{multiset}
multiset里
e
r
a
s
e
erase
erase删除值是将全部为这个值的删去。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#define ID multiset<int>::iterator
using namespace std;
multiset<int> F[50010];
int n,m,len=0,tot,limit,ans;
struct node{int x,y,z,next;} a[100010];
int last[50010];
void ins(int x,int y,int z)
{
a[++len]=(node){x,y,z,last[x]}; last[x]=len;
}
void dfs(int x,int fa)
{
for(int i=last[x];i;i=a[i].next)
{
int y=a[i].y;
if(y==fa) continue;
dfs(y,x);
if(F[y].size())
{
int op=*(--F[y].end());
if(op+a[i].z>=limit) tot++; else F[x].insert(op+a[i].z);
}
else
{
if(a[i].z>=limit) tot++; else F[x].insert(a[i].z);
}
}
int ma=-1;
while(F[x].size())
{
int op=*F[x].begin();
F[x].erase(F[x].begin());
ID pos=F[x].lower_bound(limit-op);
if(pos!=F[x].end()) F[x].erase(pos),tot++; else ma=op;
}
if(ma!=-1) F[x].insert(ma);
}
bool check(int x)
{
for(int i=1;i<=n;i++)
F[i].clear();
tot=0;
limit=x;
dfs(1,0);
return tot>=m;
}
int main()
{
int x,y,z,l=0,r=0,mid;
scanf("%d %d",&n,&m);
for(int i=1;i<n;i++)
{
scanf("%d %d %d",&x,&y,&z);
ins(x,y,z),ins(y,x,z);
r+=z;
}
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) l=mid+1,ans=mid; else r=mid-1;
}
printf("%d",ans);
}