C/C++教程

P2680 [NOIP2015 提高组] 运输计划 【二分+LCA+树上差分】

本文主要是介绍P2680 [NOIP2015 提高组] 运输计划 【二分+LCA+树上差分】,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

题目描述

公元 \(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\)。

数据保证

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

输出格式

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

样例输入

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

样例输出

11

对于 \(100\%\) 的数据,保证:\(1 \leq a_i,b_i \leq n\),\(0 \leq t_i \leq 1000\),\(1 \leq u_i,v_i \leq n\)。


人话:给定一棵带点权的树,有 \(m\) 个从 \(u_i\) 到 \(v_i\) 的路径,让你将一条边变为 \(0\) ,使其路径最大值最小。

最大值最小,一听就是二分,考虑二分这个路径的最小值, 有什么可以 check 的性质吗?

首先我们可以预处理出 \(u_i\) 到 \(v_i\) 的经历的权值和,从大到小排个序,我们的 mid 肯定将这个有序之后的数组分为两部分,权值小于 mid 的部分和大于 mid 的部分。

小于 mid 的部分我们自然不同管他,因为它自然满足小于 mid 的限制,但大于 mid 的部分,我们需要在他们的交集边取一个最大值,减去最大的边权,判断这个值是否小于 mid 大于即使非法。

为什么要取交集?因为不是交集的边不能保证最大的减去它是合法情况且不能保证所有的大于 mid 的边都能减去这个值,所以必须取交集。

怎么取交集?树上边差分即可,边权化点权后,满足条件的点被覆盖的次数正好就是大于 mid 的边的个数,边差分维护就可以了。还有,边差分是 \(st[lca(u,v)]\) 减 \(2\) 捏,我真的服气我的记性了。

Code.

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+20;
struct node
{
	int u,v,lca,dis;
	bool operator < (const node &o) const {
		return dis > o.dis;
	}
} q[N];
int l,r,n,m,h[N],ne[N<<1],e[N<<1],w[N<<1],pl[N],idx,dep[N],top[N],sz[N],son[N],fa[N],res,st[N],yl[N];
void add(int u,int v,int c) {ne[++idx]=h[u],e[idx]=v,w[idx]=c,h[u]=idx;}
void dfs(int u,int father,int depth)
{
	dep[u]=depth; fa[u]=father; sz[u]=1;
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i]; if(j == father) continue ;
		yl[j]=yl[u]+w[i]; pl[j]=w[i]; dfs(j,u,depth+1); sz[u]+=sz[j];
		if(sz[son[u]] < sz[j]) son[u]=j;
	}
}
void dfs2(int u,int t)
{
	top[u]=t; if(! son[u]) return ; dfs2(son[u],t);
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i]; if(j == fa[u] || j == son[u]) continue ;
		dfs2(j,j);
	}
}
int lca(int u,int v)
{
	while(top[u] != top[v])
	{
		if(dep[top[u]] < dep[top[v]]) swap(u,v);
		u=fa[top[u]];
	}
	return dep[u] < dep[v] ? u : v;
}
void dfs3(int u,int father)
{
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i]; if(j == father) continue ;
		dfs3(j,u); st[u]+=st[j];
	}
}
bool check(int mid)
{
	int cnt=0,mx=0; memset(st,0,sizeof(int)*(n+10));
	for(int i=1;i<=m;i++)
	{
		if(q[i].dis <= mid) break;
		st[q[i].u]++; st[q[i].v]++; st[q[i].lca]-=2; cnt++;
	} dfs3(1,0);
	for(int i=1;i<=n;i++) if(st[i] == cnt) mx=max(mx,pl[i]);
	return q[1].dis-mx <= mid;
}
int main()
{
	memset(h,-1,sizeof h); scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++)
	{
		int u,v,c; scanf("%d%d%d",&u,&v,&c); l=max(l,c);
		add(u,v,c); add(v,u,c); 
	} dfs(1,0,1); dfs2(1,1);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&q[i].u,&q[i].v);
		q[i].lca=lca(q[i].u,q[i].v); q[i].dis=yl[q[i].u]+yl[q[i].v]-yl[q[i].lca]*2;
	}
	stable_sort(q+1,q+1+m); r=q[1].dis; l=r-l;
	while(l <= r)
	{
		int mid = l + r >> 1;
		if(check(mid)) res=mid,r=mid-1;
		else l=mid+1;
	}
	printf("%d",res);
	return 0;
}
这篇关于P2680 [NOIP2015 提高组] 运输计划 【二分+LCA+树上差分】的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!