賽道修建[NOIP2018][二分+樹形DP]
阿新 • • 發佈:2018-11-23
考場上拿的55pts , 還是比較好想 ---- m=1:直徑 , 鏈:二分答案+dis字首和驗證 , 連向1:排序+貪心
正解: 先二分一個答案 然後DP
f[i] 表示i子樹中大於k的路徑條數最多是多少
g[i] 表示i子樹不在那些路徑上的最長路徑
每次將g[i]+w 放到multiset中 , 大於k , f[i]++
然後用lower_bound找兩個最接近的合併 , 每合併一個f[i]++
開始用的set 然後一直wa (查了2h [mmp])
上網看了看它們的區別 , multiset 中一個值可以重複出現,而set不行
也就是說我set中插入了兩個路徑 , 如果它們值一樣 , 就自動抵消了一個
#include<bits/stdc++.h> #define N 100005 #define M N*2 #define inf 0x3fffffff #define It multiset<int>::iterator using namespace std; int first[N],next[M],to[M],w[M],tot; int n,m,l=1,r,f[N],g[N]; multiset<int> S; int read(){ int cnt=0,f=1;char ch=0; while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))cnt=cnt*10+(ch-'0'),ch=getchar(); return cnt; } void add(int x,int y,int z){ next[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z; } void dfs(int u,int fa,int k){ for(int i=first[u];i;i=next[i]){ int t=to[i]; if(t==fa) continue; dfs(t,u,k) , f[u] += f[t]; } for(int i=first[u];i;i=next[i]){ int t=to[i]; if(t==fa) continue; S.insert(g[t]+w[i]) ; } while(S.size()){ int now = *S.rbegin(); if(now>=k) f[u]++ , S.erase(S.find(now)); else break; } while(S.size()){ int now = *S.begin(); S.erase(S.begin()); It x = S.lower_bound(k - now); if(x==S.end()) g[u] = now; else f[u]++ , S.erase(x); } S.clear(); } bool check(int k){ memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); dfs(1,0,k); return f[1]>=m; } int main(){ n=read(),m=read(); for(int i=1;i<n;i++){ int x=read(),y=read(),z=read(); add(x,y,z),add(y,x,z); r+=z; } while(l<r){ int mid=(l+r+1)>>1; if(check(mid)) l=mid; else r=mid-1; } printf("%d",l); return 0; }