【NOIP2015提高组】运输计划

47 篇文章 13 订阅
18 篇文章 0 订阅

题目背景

NOIP2015 提高组 Day2 T3

题目描述

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

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

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

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

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

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

输入格式

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

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

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

输出格式

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

输出

11

备注

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

【数据范围】 
所有测试数据的范围和特点如下表所示:

请注意常数因子带来的程序效率上的影响。

 

解析:
       看了半天官方题解感觉一脸懵逼,最后只看懂了二分+树链剖分+差分的做法。

       具体做法就是:

       1.算出每对u_{i}v_{i}之间的最短距离,树剖可顺带解决。

       2.将距离按照降序排序。

       3.二分答案。 对路径长度大于mid 的路径全部打上标记并计算大于 mid 的边的条数 k,为了方便可将边的信息压到点上。找出被标记 k 次且最大的那条边,如果条边减去这权值是否小于 mid 就可以下调边界,否则上调。

       为什么是取 k 条路径的交呢?
       证明:
       因为如果只取 k - 1 条那么必然有一个路径是无法被减到 mid 以下。

       这样做跑得很快,根本不用考虑常数的问题。

       做了这道题才发现以前树剖的板子有点问题。。。

 

代码:

#include <bits/stdc++.h>
using namespace std;

const int Max=300005;
int n,m,s,ans,tot,l,r,mid;
int first[Max],son[Max],pos[Max],top[Max],depth[Max],father[Max],size[Max],dis[Max],num[Max],len[Max],val[Max],v[Max];
struct shu{int to,next,len;}edge[Max<<1];
struct bian{int x,y,len;}d[Max];
inline int get_int()
{
	int x=0,f=1;
	char c;
	for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
	if(c=='-') f=-1,c=getchar();
	for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
	return x*f;
}
inline void build(int x,int y,int z){edge[++s].next=first[x],first[x]=s,edge[s].to=y,edge[s].len=z;}
inline void dfs1(int point,int fa)
{
	size[point]=1,father[point]=fa,depth[point]=depth[fa]+1;
	for(int u=first[point];u;u=edge[u].next)
	{
	  int to=edge[u].to;
	  if(to==fa) continue;
	  val[to]=edge[u].len,dis[to]=dis[point]+edge[u].len;
	  dfs1(to,point);
	  size[point]+=size[to];
	  if(size[to] > size[son[point]]) son[point]=to;
	}
}
inline void dfs2(int point,int fa)
{
	if(son[point])
	{
	  pos[son[point]]=++tot,len[tot]=val[son[point]],top[son[point]]=top[point];
	  dfs2(son[point],point);
	}
	for(int u=first[point];u;u=edge[u].next)
	{
	  int to=edge[u].to;
	  if(top[to]) continue;
	  pos[to]=++tot,top[to]=to;
	  dfs2(to,point);
	}
}
inline int LCA(int x,int y)
{
	while(top[x]!=top[y])
	{
	  if(depth[top[x]] < depth[top[y]]) x^=y,y^=x,x^=y;
	  x=father[top[x]];
	}
	if(depth[x] > depth[y]) swap(x,y);
	return x;
}
inline bool comp(const bian &a,const bian &b){return a.len>b.len;}
inline void solve(int x,int y)
{
	while(top[x]!=top[y])
	{
	  if(depth[top[x]] < depth[top[y]]) x^=y,y^=x,x^=y;
	  num[pos[top[x]]+1]++,num[pos[x]+1]--;
	  x=father[top[x]];
	}
	if(x==y) return;
	if(depth[x] > depth[y]) x^=y,y^=x,x^=y;
	num[pos[x]+1]++,num[pos[y]+1]--;
}
inline int mx(int x,int y){return x < y ? y : x;}
inline int check(int mid)
{
	int k=0,x=0,sum=0;
	while(d[k+1].len>mid) k++;
	if(v[k]) return v[k];
	memset(num,0,sizeof(num));
	for(int i=1;i<=k;i++) solve(d[i].x,d[i].y);
	for(int i=1;i<=n;i++){sum+=num[i];if(sum == k) x=mx(x,len[i]);}
	v[k]=x;
	return x;
}
inline void print(int x){if(x>9) print(x/10);putchar('0'+x%10);}
int main()
{
	n=get_int(),m=get_int();
	for(int i=1;i<=n-1;i++)
	{
	  int x=get_int(),y=get_int(),z=get_int();
	  build(x,y,z),build(y,x,z);
	}
	top[1]=tot=pos[1]=1;
	dfs1(1,0),dfs2(1,0);
	for(int i=1;i<=m;i++)
	{
	  d[i].x=get_int(),d[i].y=get_int();
	  d[i].len=dis[d[i].x]+dis[d[i].y]-2*dis[LCA(d[i].x,d[i].y)];
	}
	sort(d+1,d+m+1,comp);
	l=0,r=d[1].len;
	while(l<r)
	{
	  mid=(l+r)>>1;
	  (d[1].len - check(mid) <= mid) ? r=mid : l=mid+1;
	}
	print(r);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值