傳送門算法
將路徑按長度排序,設最長的路徑長度爲\(L\)。對遞增的\(k\),求前\(k\)大路徑的交中邊的最大值\(M\),第\(k+1\)長路徑的長度爲\(N\),答案與\(\max\{L-M,N\}\)取最小值。spa
正確性:考慮一個合法的解,設它在前\(k\)條路徑上,則其必不在第\(k+1\)條(若是有的話)路徑上。code
設要求\(P_1=(u_1, v_1)\)以及\(P_2=(u_2,v_2)\)這兩條路徑的交\(P=P_1\cap P_2=(u,v)\)。排序
先考慮\(P_1\),\(P_2\)都是鏈的狀況(不妨設\(v_1,v_2\)分別爲\(u_1,u_2\)的祖先)。get
此時,考慮入棧序\(I\)與出棧序\(O\):一個點\(x\)在\(P_i\)上當且僅當其知足\(I_{u_i} \le I_x \le I_{v_i}\)與\(O_{u_i} \ge O_x \ge O_{v_i}\)。(能夠用二維線段樹解決(霧))io
考慮實際圖形(設\(dep_{v_1}\le dep_{v_2}\))。class
先考慮在何時交爲空。顯然,在\(v_1\)與\(v_2\)不存在一個在另外一個的子樹裏的時候交爲空。可是,它不是充分必要條件(例如,\(v_1\)是\(v_2\)的某個其餘兒子)。原題設的充分必要條件是「\(v_1\)不在\(P_2\)上」,因此咱們在預處理時處理出入棧出序列,查詢時直接比較便可。易知\(P\)中\(u=LCA(u_1,v_1), v = v_1\)。route
若\(P_1,P_2\)都不簡單:暴力轉化成四對鏈的交的並。(常數極大)查詢
注意題面裏面沒有保證\(u\ne v\)。di
route merge(route R, route r) { if (R.empty() && r.empty()) return NO; else if (R.empty()) return r; else if (r.empty()) return R; else { R.clean(), r.clean(); if (R.v == r.v) return (route){R.u, r.u}; else assert(0); } } route inter(route R, route r) { if (R.empty() || r.empty()) return NO; R.clean(), r.clean(); int L = lca(R.u, R.v).first, l = lca(r.u, r.v).first; if (L == R.v && l == r.v) { if (dep[L] < dep[l]) { swap(L, l); swap(R, r); } if (din[r.v] <= din[R.v] && din[R.v] <= din[r.u] && dout[r.v] >= dout[R.v] && dout[R.v] >= dout[r.u]) return route(lca(R.u, r.u).first, L); else return NO; } if (L == R.v) { swap(R, r); swap(L, l); } return merge(inter(route(R.u, L), r), inter(route(R.v, L), r)); }
因爲算法會把路徑的交做爲下一次的路徑,因此若是對每條邊進行考慮的話,複雜度有均攤保證。
首先,找出長度最大的路徑上的全部邊,將它們放入大根堆中(鍵值爲邊權)。
以後,從大到小枚舉路徑,考慮當前堆中的最大邊是否在當前路徑中,不在則彈出,以此類推。
代碼難度較小……
由於最小值有點難求(且顯然知足單調性),考慮將其轉化爲斷定性問題。
因爲二分答案只有\(\log\)次,因此求線段的交能夠\(O(m+n)\)。對一條路徑\((u,v)\),tag[u]++, tag[v]++, tag[lca(u,v)]-=2
便可。
以後,dfs
整棵樹,通過一條邊時判斷是否被全部選擇的路徑覆蓋便可。(一條邊被路徑覆蓋的次數=末端點子樹的權值和)