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;
}

猜你喜欢

转载自blog.csdn.net/INTEGRATOR_37/article/details/83547864