Luogu5021 賽道修建
阿新 • • 發佈:2020-09-10
https://www.luogu.com.cn/problem/P5021
圖論
十分簡單的題目,想起NOIP2018時嘆其為不可做題,一點一點啃暴力分(自閉)
很明顯,需要二分答案,設二分到的值為\(mid\)
對於每個節點考慮,倘若有一條鏈經過節點\(u\)和其父親之間的連邊,那麼我們只能有一條鏈延伸上去,在滿足取得的鏈數最多的情況下,我們只需要讓延伸上去的鏈最長即可,設延伸上去的最長鏈長為\(mxl_u\)
怎麼操作?對於\(u\)的子節點\(v\),他們都存在一個\(mxl_v\),當然,\(mxl_v\)要加上\(edge_{u,v}\)之後進行操作,然後我們將這些數當成數列\(A\)
對於已經\(\ge mid\)
我們從小到大匹配,對於\(a_i\),如果有\(\ge mid-a_i\)的數,直接匹配滿足條件的最小的那一個,答案增加,否則\(a_i\)作為\(mxl_u\)的候選
要支援動態刪除,可以直接利用\(multiset\),然後就\(AC\)了
\(Code:\)
#include<iostream> #include<cstdio> #include<algorithm> #include<set> #define N 50005 using namespace std; int n,m,x,y,z,tot,mxl[N],fr[N],nxt[N << 1],d1[N << 1],d2[N << 1]; int l=0,r=0,mid,ans=0,kz; multiset<int>s; void add(int x,int y,int z) { tot++; d1[tot]=y; d2[tot]=z; nxt[tot]=fr[x]; fr[x]=tot; } void dfs(int u,int F) { for (int i=fr[u];i && kz>0;i=nxt[i]) { int v=d1[i]; if (v==F) continue; if (!nxt[fr[v]]) { mxl[v]=d2[i]; continue; } dfs(v,u); mxl[v]+=d2[i]; } if (kz<=0) return; s.clear(); mxl[u]=0; for (int i=fr[u];i;i=nxt[i]) { int v=d1[i]; if (v==F) continue; if (mxl[v]>=mid) kz--; else s.insert(mxl[v]); } while (!s.empty() && kz>0) { multiset<int> :: iterator it=s.begin(); int o=*it; s.erase(it); multiset<int> :: iterator it2=s.lower_bound(mid-o); if (it2!=s.end()) { kz--; s.erase(it2); continue; } mxl[u]=max(mxl[u],o); } } bool check() { kz=m; dfs(1,0); if (mxl[1]>=mid) kz--; return kz<=0; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<n;i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z),add(y,x,z); r+=z; } r/=m; while (l<=r) { mid=(l+r) >> 1; if (check()) { ans=mid; l=mid+1; } else r=mid-1; } printf("%d\n",ans); return 0; }