[NOIP2015] 運輸計劃

傳送門算法

解答

思路1 直接求樹上路徑的交

將路徑按長度排序,設最長的路徑長度爲\(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));
}

神犇wygz的作法

因爲算法會把路徑的交做爲下一次的路徑,因此若是對每條邊進行考慮的話,複雜度有均攤保證

首先,找出長度最大的路徑上的全部邊,將它們放入大根堆中(鍵值爲邊權)。

以後,從大到小枚舉路徑,考慮當前堆中的最大邊是否在當前路徑中,不在則彈出,以此類推。

代碼難度較小……

思路2 二分答案(求長度大於答案的全部線段的交中的最大邊權來驗證答案)

由於最小值有點難求(且顯然知足單調性),考慮將其轉化爲斷定性問題。

因爲二分答案只有\(\log\)次,因此求線段的交能夠\(O(m+n)\)。對一條路徑\((u,v)\)tag[u]++, tag[v]++, tag[lca(u,v)]-=2便可。

以後,dfs整棵樹,通過一條邊時判斷是否被全部選擇的路徑覆蓋便可。(一條邊被路徑覆蓋的次數=末端點子樹的權值和)

LOJ最短代碼

相關文章
相關標籤/搜索