1. 程式人生 > >BZOJ 4326 運輸計劃(LCA +樹上差分)

BZOJ 4326 運輸計劃(LCA +樹上差分)

任重而道遠

Description

公元 2044 年,人類進入了宇宙紀元。L 國有 n 個星球,還有 n?1 條雙向航道,每條航道建立在兩個星球之間,

這 n-1 條航道連通了 L 國的所有星球。小 P 掌管一家物流公司, 該公司有很多個運輸計劃,每個運輸計劃形如

:有一艘物流飛船需要從 ui 號星球沿最快的宇航路徑飛行到 vi 號星球去。顯然,飛船駛過一條航道是需要時間

的,對於航道 j,任意飛船駛過它所花費的時間為 tj,並且任意兩艘飛船之間不會產生任何干擾。為了鼓勵科技

創新, L 國國王同意小 P 的物流公司參與 L 國的航道建設,即允許小P 把某一條航道改造成蟲洞,飛船駛過蟲

洞不消耗時間。在蟲洞的建設完成前小 P 的物流公司就預接了 m 個運輸計劃。在蟲洞建設完成後,這 m 個運輸

計劃會同時開始,所有飛船一起出發。當這 m 個運輸計劃都完成時,小 P 的物流公司的階段性工作就完成了。如

果小 P 可以自由選擇將哪一條航道改造成蟲洞, 試求出小 P 的物流公司完成階段性工作所需要的最短時間是多

少?

Input

第一行包括兩個正整數 n,m,表示 L 國中星球的數量及小 P 公司預接的運輸計劃的數量,星球從 1 到 n 編號。

接下來 n-1 行描述航道的建設情況,其中第 i 行包含三個整數 ai,bi 和 ti,

表示第 i 條雙向航道修建在 ai 與 bi 兩個星球之間,任意飛船駛過它所花費的時間為 ti。

接下來 m 行描述運輸計劃的情況,其中第 j 行包含兩個正整數 uj 和 vj,表示第 j 個運輸計劃是從 uj 號星球飛往 vj號星球。

資料保證 1≤ui,vi≤n ,n,m<=300000

資料保證 1≤ai,bi≤n 且 0≤ti≤1000。

Output

輸出檔案只包含一個整數,表示小 P 的物流公司完成階段性工作所需要的最短時間。

Sample Input

6 3
1 2 3
1 6 4
3 1 7
4 3 6
3 5 5
3 6
2 5
4 5

Sample Output

11
將第 1 條航道改造成蟲洞: 則三個計劃耗時分別為:11,12,11,故需要花費的時間為 12。
將第 2 條航道改造成蟲洞: 則三個計劃耗時分別為:7,15,11,故需要花費的時間為 15。
將第 3 條航道改造成蟲洞: 則三個計劃耗時分別為:4,8,11,故需要花費的時間為 11。
將第 4 條航道改造成蟲洞: 則三個計劃耗時分別為:11,15,5,故需要花費的時間為 15。
將第 5 條航道改造成蟲洞: 則三個計劃耗時分別為:11,10,6,故需要花費的時間為 11。
故將第 3 條或第 5 條航道改造成蟲洞均可使得完成階段性工作的耗時最短,需要花費的時間為 11。

AC程式碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N = 3e5 + 5, P = 22;
struct Edge {int tov, nxt, val;} e[N << 1];
struct Node {
	int u, v, lca, val;
	bool operator < (const Node &a) const {
		return val > a.val;
	}
}q[N];
int head[N], anc[N][P + 2], w[N][P + 2], s[N], dep[N], dis[N];
int n, m, num;

int read () {
	int x = 0, f = 0; char c = getchar ();
	while (!isdigit (c)) f |= (c == '-'), c = getchar ();
	while (isdigit (c)) x = x * 10 + c - '0', c = getchar ();
	return f ? -x : x;
}

void add_edge (int u, int v, int val) {
	e[++num] = (Edge) {v, head[u], val}, head[u] = num;
	e[++num] = (Edge) {u, head[v], val}, head[v] = num;
}

void dfs (int u) {
	dep[u] = dep[anc[u][0]] + 1;
	for (int i = head[u]; i; i = e[i].nxt) {
		int v = e[i].tov;
		if (v == anc[u][0]) continue;
		anc[v][0] = u, dis[v] = dis[u] + e[i].val;
		dfs (v);
	}
}

void init () {
	dfs (1);
	for (int p = 1; p <= P; p++)
		for (int u = 1; u <= n; u++)
			anc[u][p] = anc[anc[u][p - 1]][p - 1];
}

int Lca (int u, int v) {
	if (dep[u] < dep[v]) swap (u, v);
	for (int p = P; p >= 0; p--)
		if (dep[anc[u][p]] >= dep[v]) u = anc[u][p];
	if (u == v) return u;
	for (int p = P; p >= 0; p--)
		if (anc[u][p] != anc[v][p]) {
			u = anc[u][p];
			v = anc[v][p];
		}
	return anc[u][0];
}

void get (int u) {
	for (int i = head[u]; i; i = e[i].nxt) {
		int v = e[i].tov;
		if (v == anc[u][0]) continue;
		get (v);
		s[u] += s[v];
	}
}

bool check (int x) {
	memset (s, 0, sizeof (s));
	int tot = 0, sww = 0, f = 1, jud = 0;
	for (int i = 1; q[i].val > x; i++) {
		s[q[i].u]++, s[q[i].v]++;
		s[q[i].lca] -= 2;
		tot++;
	}
	get (1);
	for (int i = 1; i <= n; i++)
		if (s[i] == tot) {
			sww = max (sww, dis[i] - dis[anc[i][0]]);
			jud = 1;
		}
	if (!jud) return false;
	for (int i = 1; i <= tot; i++)
		if (q[i].val - sww > x) {
			f = 0; break;
		}
	return f;
}

int main () {
	n = read (), m = read ();
	for (int i = 1; i < n; i++) {
		int u = read (), v = read (), val = read ();
		add_edge (u, v, val);
	}
	init ();
	for (int i = 1; i <= m; i++) {
		int u = read (), v = read ();
		int ca = Lca (u, v);
		q[i] = (Node) {u, v, ca, dis[u] + dis[v] - 2 * dis[ca]};
	}
	sort (q + 1, q + m + 1);
	int lf = 0, rg = q[1].val;
	while (lf <= rg) {
		int mid = lf + rg >> 1;
		if (check (mid)) rg = mid - 1;
		else lf = mid + 1;
	}
	printf ("%d\n", lf);
	return 0;
}