洛谷P2680 运输计划

来源:互联网 发布:ppt chm 制作软件 编辑:程序博客网 时间:2024/06/07 11:47

题目背景

公元 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 300010using 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;}