[NOIp2018提高組]賽道修建
阿新 • • 發佈:2018-11-10
[NOIp2018提高組]賽道修建
題目大意:
給你一棵\(n(n\le5\times10^4)\)個結點的樹,從中找出\(m\)個沒有公共邊的路徑,使得第\(m\)長的路徑最長。問第\(m\)長的路徑最長可以是多少。
思路:
二分答案+樹形DP。\(f[x]\)表示以\(x\)為根的子樹中最多能找出幾個長度\(\ge k\)的路徑。\(g[x]\)表示去掉已經滿足的路徑,從\(x\)子樹內往上連的最長的路徑有多長。
轉移時將所有子結點的貢獻\(g[y]+w\)排序。若貢獻已經\(\ge k\),那麼就直接計入答案。否則從小到大列舉每一個貢獻,找到能與其配對的最小的貢獻,計入答案。如果找不到能與之配對的貢獻,那麼就用它來更新\(g[x]\)
時間複雜度\(\mathcal O(n\log n\log\)值域\()\)。
原始碼:
#include<set> #include<cstdio> #include<cctype> #include<vector> #include<climits> #include<algorithm> inline int getint() { register char ch; while(!isdigit(ch=getchar())); register int x=ch^'0'; while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); return x; } const int N=5e4+1; struct Edge { int to,w; }; std::vector<Edge> e[N]; inline void add_edge(const int &u,const int &v,const int &w) { e[u].push_back((Edge){v,w}); e[v].push_back((Edge){u,w}); } std::multiset<int> t; int f[N],g[N],len; void dfs(const int &x,const int &par) { f[x]=0; for(auto &j:e[x]) { const int &y=j.to; if(y==par) continue; dfs(y,x); f[x]+=f[y]; } for(auto &j:e[x]) { const int &y=j.to; if(y==par) continue; t.insert(g[y]+j.w); } while(!t.empty()) { const int u=*t.rbegin(); if(u>=len) { f[x]++; t.erase(t.find(u)); } else { break; } } g[x]=0; while(!t.empty()) { const int u=*t.begin(); t.erase(t.begin()); auto p=t.lower_bound(len-u); if(p==t.end()) { g[x]=u; } else { t.erase(p); f[x]++; } } t.clear(); } inline int calc(const int &k) { len=k; dfs(1,0); return f[1]; } int main() { const int n=getint(),m=getint(); int l=INT_MAX,r=0; for(register int i=1;i<n;i++) { const int u=getint(),v=getint(),w=getint(); add_edge(u,v,w); l=std::min(l,w); r+=w; } while(l<=r) { const int mid=(l+r)>>1; if(calc(mid)>=m) { l=mid+1; } else { r=mid-1; } } printf("%d\n",l-1); return 0; }