1. 程式人生 > 實用技巧 >賽道修建——二分答案

賽道修建——二分答案

題目連結:https://www.luogu.com.cn/problem/P5021

分析:

題目大意就不講了。此題很多人說一眼就知道是二分,那就當我也這麼認為吧。此題思考的關鍵在於不能重邊。先設二分的值為k,我們思考一個狀態:此時搜到一個點,暫時不考慮下面回溯上來的值,當該值加上其對應的邊權大於等於k,則直接統計到答案裡,剩餘的不滿k的值先存起來。我們知道剩餘的這些邊中還是會存在滿足大於等於k的情況,只要任意兩條加在一起大於等於k就可以計入答案。(記住這裡要用貪心,即對於每條邊選擇可行方案裡最小的)接下來同樣按著貪心,選擇一條剩餘邊裡最大的一條回溯上去,該值即上文回溯上來的值。看不懂的看程式碼食用更佳哦!

補充:程式碼裡用到了set庫裡的multiset,這裡使用能更佳方便地實現程式碼,不會的建議瞭解一下。

程式碼:

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<set>
using namespace std;
#define int long long
#define R register
inline int read(){
    int a=0,b=1;char c=getchar();
    while(!isdigit(c)){if
(c=='-')b=-1;c=getchar();} while(isdigit(c)){a=a*10+c-'0';c=getchar();} return a*b; } multiset<int>::iterator it; const int N=5e4+50,M=2e5+50; int n,m,tot,cnt,h[N],ver[M],nx[M],ed[M],ave,l,r,ans; void add(int u,int v,int z){ ver[++tot]=v;ed[tot]=z; nx[tot]=h[u];h[u]=tot; } int dfs(int
x,int fa,int kkk){ multiset<int>s; for(R int i=h[x],dis;i;i=nx[i]){ int v=ver[i],z=ed[i]; if(v==fa)continue; dis=z+dfs(v,x,kkk); if(dis>=kkk)cnt++; else s.insert(dis); } int maxn=0; while(s.size()){ int p=*s.begin(); s.erase(s.begin()); it=s.lower_bound(kkk-p); if(it!=s.end()){ cnt++; s.erase(it); } else{ maxn=max(maxn,p); } } return maxn; } bool check(int kkk){ cnt=0; dfs(1,0,kkk); if(cnt>=m)return true; else return false; } signed main(){ n=read();m=read(); int u,v,z; for(R int i=1;i<n;i++){ u=read();v=read();z=read(); add(u,v,z);add(v,u,z); ave+=z; } ave/=m; l=1,r=ave; int mid; while(l<=r){ mid=(l+r)>>1; if(check(mid))ans=mid,l=mid+1; else r=mid-1; } printf("%lld\n",ans); return 0; }