【noip2018】【luogu5021】賽道修建
題目描述
C 城將要舉辦一系列的賽車比賽。在比賽前,需要在城內修建 m 條賽道。
C 城一共有 n 個路口,這些路口編號為 1,2,…,n,有 n−1 條適合於修建賽道的雙向通行的道路,每條道路連線著兩個路口。其中,第 i 條道路連線的兩個路口編號為 ai 和 bi,該道路的長度為 li。藉助這 n−1 條道路,從任何一個路口出發都能到達其他所有的路口。
一條賽道是一組互不相同的道路
目前賽道修建的方案尚未確定。你的任務是設計一種賽道修建的方案,使得修建的 m 條賽道中長度最小的賽道長度最大(即 m 條賽道中最短賽道的長度儘可能大)
輸入輸出格式
輸入格式:
輸入檔案第一行包含兩個由空格分隔的正整數 n,
接下來 n−1 行,第 i 行包含三個正整數 ai,bi,li,表示第 i 條適合於修建賽道的道 路連線的兩個路口編號及道路長度。保證任意兩個路口均可通過這 n−1 條道路相互到達。每行中相鄰兩數之間均由一個空格分隔。
輸出格式:
輸出共一行,包含一個整數,表示長度最小的賽道長度的最大值。
輸入輸出樣例
輸入樣例#1: 複製7 1 1 2 10 1 3 5 2 4 9 2 5 8 3 6 6 3 7 7
9 3 1 2 6 2 3 3 3 4 5 4 5 10 6 2 4 7 2 9 8 4 7 9 4 4輸出樣例#2: 複製
15
輸出樣例#1: 複製
31
【資料規模與約定】
對於所有的資料, 2≤n≤50,000, 1≤m≤n−1, 1≤ai,bi≤n, 1≤li≤10,000。
題解:
題意即求一個k段不相交路徑長度最小值的最大值;
二分這個最大的最小值mid,那麼要求判斷是否有一種方案可以分出>=k條路徑使得權值都>=mid;
樹形dp : 當做到節點u,要麼u的所有兒子都一定成為了路徑,要麼至多有一條可以向上延伸的路徑;
這樣如果可以在u形成>=mid的路徑,那麼一定比此時不形成路徑往上連更優;
記錄每個點可以往上連的權值val,考慮u的兒子v,u,v之間的邊為E[i] (程式碼風格,忍受一下。。)
val[v]+E[i].w >= mid 直接統計為一條合法路徑;
否則把左邊存在一個數組裡,可以知道要形成路徑只能兩兩配對,排序後二分可以確定最大的對數;
注意未配對的最大的數可能可以替換已配對的權值,找到最大可替換數設定成val[u](注意細節)
複雜度 $O(n log^2 n)$
1 #include<cstdio> 2 #include<iostream> 3 #include<vector> 4 #include<algorithm> 5 #define inf 0x3f3f3f3f 6 #define rg register 7 #define il inline 8 #define Run(i,l,r) for(int i=l;i<=r;i++) 9 using namespace std; 10 const int N=50500; 11 int n,k,o,hd[N],mid,val[N],sum,p[N]; 12 vector<int>g[N]; 13 struct Edge{int v,nt,w;}E[N<<1]; 14 void adde(int u,int v,int w){ 15 E[o]=(Edge){v,hd[u],w};hd[u]=o++; 16 E[o]=(Edge){u,hd[v],w};hd[v]=o++; 17 } 18 bool check2(int u,int Mid){ 19 int cnt=g[u].size(); 20 int p=cnt-(Mid<<1); 21 for(int i=1;i<=Mid;i++){ 22 if(g[u][p+i-1]+g[u][cnt-i]<mid)return false; 23 } 24 return true; 25 } 26 void dfs(int u,int fa){ 27 g[u].clear(); 28 val[u]=0; 29 for(int i=hd[u];i;i=E[i].nt){ 30 int v=E[i].v; 31 if(v==fa)continue; 32 dfs(v,u); 33 int t=E[i].w+val[v]; 34 if(t>=mid)sum++; 35 else g[u].push_back(t); 36 } 37 int cnt=(int)g[u].size(); 38 if(!cnt)return; 39 sort(g[u].begin(),g[u].end()); 40 int l=0,r=cnt>>1; 41 while(l<r){ 42 int Mid=(l+r+1)>>1; 43 if(check2(u,Mid))l=Mid; 44 else r=Mid-1; 45 } 46 sum+=l; 47 if((l<<1)==cnt)return; 48 for(int i=0;i<l;i++){ 49 int t1=cnt-(l<<1)+i; 50 int t2=cnt-1-i; 51 p[t1]=t2; 52 p[t2]=t1; 53 } 54 int t; 55 for(t=cnt-(l<<