背景:
一年前自己還是太菜了。
雖然現在也菜。
題目傳送門:
https://www.luogu.org/problem/P5021
題意:
一棵樹,選擇條邊鋪設,要求這些邊不能有交集,且不允許掉頭(我們認爲它是從一點修到另外一點的),現在求這些賽道的最短值的最大值。
思路:
現在看來還是一個眼題的。
二分答案。
考慮兒子的信息通過當前的邊來合併。
若當前已經可以更新到,直接方案數加即可。
若當前不能更新到,考慮從小到大選出儘可能小的能與它配對達到的,將這兩個配對在一起(相當與這兩條路徑通過當前點連在一起),方案數加。
最後傳上去最大的即可。
可用維護即可。
注意裏刪除值是將全部爲這個值的刪去。
代碼:
#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);
}