传送门算法
将路径按长度排序,设最长的路径长度为\(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
整棵树,通过一条边时判断是否被全部选择的路径覆盖便可。(一条边被路径覆盖的次数=末端点子树的权值和)