【NOIP 2015 提高组】运输计划

【题目】

传送门

题目描述:

公元 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 的物流公司完成阶段性工作所需要的最短时间是多少?

输入格式:

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

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

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

输出格式:

共 1 行,包含 1 个整数,表示小 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

备注:

【样例说明】
将第 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。

20170522095514_48248

【分析】

本来想只找一道树上差分的题,然后就莫名找到这道题,然后就。。。

直接来说正解吧(反正也是结合题解写的,二分什么的真的没想到)

我们用二分答案,剩下的就是如何去 check 这个答案(mid)是否合法

首先说一下单调性,若所有运输计划能在 t_1 的时间内完成,那也能在 t_2t_2>t_1)的时间内完成,满足单调性

记录 d_i 为根到 i 的距离(根可任取),假设 z 为 x 和 y 的 lca,则 dis_{x,y}=d_x+d_y-2\cdot d_z

对于两点距离(即两点的总时间)小于等于 mid 的,可以直接丢掉,不予考虑

对于两点距离大于 mid 的,记录一下总数(用 sum 表示),用树上差分,即 s_x+1s_y+1s_z-2,做完差分后,若 s_x=sum,则 x 的那条路径就是所有路径中的公共路径,贪心找出最大的那条(用 num 表示)即可

若最后最长的距离 maxn-num ≤ mid,则符合条件;否则就不符合

这里小说一句:其实本题考得不算特别难,不过这道题的综合性比较强,把几种算法结合到一起,是道好题

【代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 500005
#define M 1000005
using namespace std;
int n,m,t,sum,num,maxn;
int v[M],w[M],next[M],first[N];
int dep[N],dis[N],father[N][25];
int a[N],s[N],uu[N],vv[N],lca[N],len[N];
void add(int x,int y,int z)
{
	t++;
	next[t]=first[x];
	first[x]=t;
	v[t]=y;
	w[t]=z;
}
void getfather(int x)
{
	int i,j;
	for(i=first[x];i;i=next[i])
	{
		j=v[i];
		if(j!=father[x][0])
		{
			father[j][0]=x;
			a[j]=w[i];
			dep[j]=dep[x]+1;
			dis[j]=dis[x]+w[i];
			getfather(j);
		}
	}
}
int Lca(int x,int y)
{
	int i;
	if(dep[x]<dep[y])  swap(x,y);
	for(i=20;i>=0;--i)
	  if(dep[father[x][i]]>=dep[y])
	    x=father[x][i];
	if(x==y)  return x;
	for(i=20;i>=0;--i)
	{
		if(father[x][i]!=father[y][i])
		{
			x=father[x][i];
			y=father[y][i];
		}
	}
	return father[x][0];
}
void dfs(int x,int pre)
{
	int i,j;
	for(i=first[x];i;i=next[i])
	{
		j=v[i];
		if(j!=pre)
		{
			dfs(j,x);
			s[x]+=s[j];
		}
	}
	if(s[x]==sum)
	  num=max(num,a[x]);
}
bool check(int mid)
{
	int i;
	num=sum=0;
	memset(s,0,sizeof(s));
	for(i=1;i<=m;++i)
	{
		if(len[i]>mid)
		{
			sum++;
			s[uu[i]]++;
			s[vv[i]]++;
			s[lca[i]]-=2;
		}
	}
	dfs(1,0);
	if(maxn-num<=mid)
	  return true;
	return false;
}
int main()
{
	int x,y,z,i,j;
	scanf("%d%d",&n,&m);
	for(i=1;i<n;++i)
	{
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z),add(y,x,z);
	}
	getfather(1);
	for(j=1;j<=20;++j)
	  for(i=1;i<=n;++i)
	    father[i][j]=father[father[i][j-1]][j-1];
	for(i=1;i<=m;++i)
	{
		scanf("%d%d",&uu[i],&vv[i]);
		lca[i]=Lca(uu[i],vv[i]);
		len[i]=dis[uu[i]]+dis[vv[i]]-2*dis[lca[i]];
		maxn=max(maxn,len[i]);
	}
	int l=0,r=maxn,mid;
	while(l<r)
	{
		mid=(l+r)>>1;
		if(check(mid))  r=mid;
		else  l=mid+1;
	}
	printf("%d",l);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_dreams/article/details/82953901