洛谷 P2680 运输计划(NOIP2015提高组)(BZOJ4326)

题目背景

公元 \(2044\) 年,人类进入了宇宙纪元。

题目描述

公元\(2044\) 年,人类进入了宇宙纪元。

L 国有 \(n\) 个星球,还有 \(n-1\) 条双向航道,每条航道建立在两个星球之间,这 \(n-1\) 条航道连通了 \(L\) 国的所有星球。

\(P\) 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 \(u_i\)​ 号星球沿最快的宇航路径飞行到 \(v_i\)​ 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 \(j\),任意飞船驶过它所花费的时间为 \(t_j\)​,并且任意两艘飞船之间不会产生任何干扰。

为了鼓励科技创新, \(L\) 国国王同意小 \(P\) 的物流公司参与 \(L\) 国的航道建设,即允许小\(P\) 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。

在虫洞的建设完成前小 P 的物流公司就预接了 \(m\) 个运输计划。在虫洞建设完成后,这 \(m\) 个运输计划会同时开始,所有飞船一起出发。当这 \(m\) 个运输计划都完成时,小 \(P\) 的物流公司的阶段性工作就完成了。

如果小 \(P\) 可以自由选择将哪一条航道改造成虫洞, 试求出小 \(P\) 的物流公司完成阶段性工作所需要的最短时间是多少?

输入输出格式

输入格式:

第一行包括两个正整数 \(n, m\),表示 \(L\) 国中星球的数量及小 \(P\) 公司预接的运输计划的数量,星球从 \(1\)\(n\) 编号。

接下来 \(n-1\) 行描述航道的建设情况,其中第 \(i\) 行包含三个整数 \(a_i, b_i\)​ 和 \(t_i\)​,表示第 \(i\) 条双向航道修建在 \(a_i\)​ 与 \(b_i\)​ 两个星球之间,任意飞船驶过它所花费的时间为 \(t_i\)​。数据保证 \(1 \leq a_i,b_i \leq n\)\(0 \leq t_i \leq 1000\)

接下来 \(m\) 行描述运输计划的情况,其中第 \(j\) 行包含两个正整数 \(u_j\)​ 和 \(v_j\)​,表示第 \(j\) 个运输计划是从 \(u_j\)​ 号星球飞往 \(v_j\)​号星球。数据保证 \(1 \leq u_i,v_i \leq n\)

输出格式:

一个整数,表示小 \(P\) 的物流公司完成阶段性工作所需要的最短时间。

输入输出样例
输入样例#1:

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

输出样例#1:

11

题解

二分答案+树上差分

显然,这个答案是具有单调性的,那么我们就可以二分答案

考虑如何写check呢?

设当前check(x)
f[u] 表示 u到它父亲这条路径被超过长度x的线路经过次数

首先我们记下长度超过x的线路数量为cnt,并把该路径经过的边 f 加1(树上差分)

然后如果存在一条边经过次数等于cnt,且最长的线路长度减去它的长度小于等于x 就可以

Code

#include<bits/stdc++.h>
#define LL long long
#define RG register
using namespace std;

inline int gi() {
    int f = 1, s = 0;
    char c = getchar();
    while (c != '-' && (c < '0' || c > '9')) c = getchar();
    if (c == '-') f = -1, c = getchar();
    while (c >= '0' && c <= '9') s = s*10+c-'0', c = getchar();
    return f == 1 ? s : -s;
}
const int N = 300010;
struct node {
	int to, next, w;
}g[N<<1];
int last[N], gl;
inline void add(int u, int v, int w) {
	g[++gl] = (node) {v, last[u], w};
	last[u] = gl;
	return ;
}
struct zz {
	int to, id;
};
vector<zz> q[N];
int cx[N], cy[N], fa[N], lca[N];

inline int find(int x) {
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}
bool vis[N];
int ff[N], dep[N], fl[N], cnt;
void tarjan(int u, int f) {
	vis[u] = 1;
	for (int i = last[u]; i; i = g[i].next) {
		int v = g[i].to;
		if (v == f) continue;
		fl[v] = g[i].w;
		dep[v] = dep[u]+g[i].w;
		tarjan(v, u);		
		fa[v] = u;
	}
	for (RG int i = 0; i < (int) q[u].size(); i++)
		if (vis[q[u][i].to] && !lca[q[u][i].id])
			lca[q[u][i].id] = find(q[u][i].to);
	return ;
}
int MAX;
void dfs(int u, int f) {
	for (RG int i = last[u]; i; i = g[i].next) {
		int v = g[i].to;
		if (v == f) continue;
		dfs(v, u);
		ff[u] += ff[v];
	}
	if (cnt == ff[u] && fl[u] > MAX) MAX = fl[u];
	return ;
}
int m;
bool check(int x) {
	cnt = 0;
	int len = 0;
	memset(ff, 0, sizeof(ff));
	for (RG int i = 1; i <= m; i++)
		if (dep[cx[i]]+dep[cy[i]]-dep[lca[i]]*2 > x) {
			ff[cx[i]]++; ff[cy[i]]++; ff[lca[i]] -= 2;
			cnt++;
			len = max(dep[cx[i]]+dep[cy[i]]-dep[lca[i]]*2, len);
		}
	MAX = 0;
	dfs(1, 0);
	if (len - MAX <= x) return 1;
	return 0;
}

int main() {
	int n = gi(), js = 0; m = gi();
	for (RG int i = 1; i < n; i++) {
		int u = gi(), v = gi(), w = gi();
		add(u, v, w); add(v, u, w);
		js += w;
	}
	for (RG int i = 1; i <= n; i++) fa[i] = i;
	for (RG int i = 1; i <= m; i++) {
		cx[i] = gi(), cy[i] = gi();
		q[cx[i]].push_back((zz){cy[i], i});
		q[cy[i]].push_back((zz){cx[i], i});
	}
	tarjan(1, 0);
	int l = 0, r = js, ans = js;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (check(mid))
			r = mid-1, ans = mid;
		else l = mid+1;
	}
	printf("%d\n", ans);
    return 0;
}


posted @ 2018-10-29 19:00  zzy2005  阅读(147)  评论(0编辑  收藏  举报