洛谷P2680 运输计划

在一个由多个星球组成的国家中,通过改造一条航道为虫洞来优化物流飞船的运输路径,实现多个运输任务的同时快速完成。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目背景

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

题目描述

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

小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物

流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。显然,飞船驶过一条航道 是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之 间不会产生任何干扰。

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

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

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

输入输出格式

输入格式:

输入文件名为 transport.in。

第一行包括两个正整数 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
输出样例#1: 
11

说明

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

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

上一句很重要!上一句很重要!!上一句很重要!!! 重要的事情说3遍!!!

这题卡常卡的特别紧。。。

用 倍增LCA+树上差分 ,但要注意差分时dfs的时间要优化,能 return 的地方尽量 return ,避免无用的搜索。。。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define MAXN 300010
using namespace std;
int n,m,c=1,l,r,mid,times,maxn=0,ans;
int head[MAXN],deep[MAXN],f[MAXN][20],lca[MAXN],s[MAXN],t[MAXN];
int dis[MAXN],num[MAXN],sum[MAXN];
bool flag;
struct node{
    int next,to,w;
}a[MAXN<<1];
inline int read(){
    int date=0,w=1;char c=0;
    while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    return date*w;
}
inline void add(int u,int v,int w){
    a[c].to=v;a[c].w=w;
    a[c].next=head[u];
    head[u]=c++;
    a[c].to=u;a[c].w=w;
    a[c].next=head[v];
    head[v]=c++;
}
inline void buildtree(int rt){
    int will;
    for(int i=head[rt];i;i=a[i].next){
        will=a[i].to;
        if(!deep[will]){
            deep[will]=deep[rt]+1;
            f[will][0]=rt;
            sum[will]=sum[rt]+a[i].w;
            buildtree(will);
        }
    }
}
inline void step(){
    for(int i=1;i<=19;i++)
    for(int j=1;j<=n;j++)
    f[j][i]=f[f[j][i-1]][i-1];
}
inline int LCA(int x,int y){
    if(deep[x]<deep[y])swap(x,y);
    for(int i=19;i>=0;i--)
    if(deep[f[x][i]]>=deep[y])
    x=f[x][i];
    if(x==y)return x;
    for(int i=19;i>=0;i--)
    if(f[x][i]!=f[y][i]){
        x=f[x][i];
        y=f[y][i];
    }
    return f[x][0];
}
inline void getnum(int now,int rt,int k){
	if(flag)return;
	int will;
	sum[now]=num[now];
	for(int i=head[now];i;i=a[i].next){
		will=a[i].to;
		if(will!=rt){
			getnum(will,now,i);
			sum[now]+=sum[will];
		}
	}
	if(sum[now]==times&&maxn-a[k].w<=mid){flag=true;return;}
}
inline bool check(){
	flag=false;times=0;
	memset(num,0,sizeof(num));
	for(int i=1;i<=m;i++)
	if(dis[i]>mid){
		num[s[i]]++;num[t[i]]++;num[lca[i]]-=2;
		times++;
	}
	getnum(1,0,0);
	return flag;
}
int main(){
    int u,v,w;
    n=read();m=read();
    for(int i=1;i<n;i++){
        u=read();v=read();w=read();
        add(u,v,w);
        sum[i]=deep[i]=0;
    }
    deep[1]=1;
    buildtree(1);
    step();
    for(int i=1;i<=m;i++){
        s[i]=read();t[i]=read();
        lca[i]=LCA(s[i],t[i]);
        dis[i]=sum[s[i]]+sum[t[i]]-2*sum[lca[i]];
        maxn=max(maxn,sum[s[i]]+sum[t[i]]-2*sum[lca[i]]);
    }
    l=0;r=maxn;
    while(l<=r){
        mid=l+r>>1;
        if(check()){r=mid-1;ans=mid;}
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值