题目描述 Description

公元 2044 年,人类进入了宇宙纪元。L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球。小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之间不会产生任何干扰。为了鼓励科技创新, L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后,这 m 个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的物流公司的阶段性工作就完成了。如果小 P 可以自由选择将哪一条航道改造成虫洞, 试求出小 P 的物流公司完成阶段性工作所需要的最短时间是多少?

输入描述 Input Description

第一行包括两个正整数 n,m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 1 到 n 编号。接下来 n−1 行描述航道的建设情况,其中第 i 行包含三个整数 ai,bi 和 ti,表示第 i 条双向航道修建在 ai 与 bi 两个星球之间,任意飞船驶过它所花费的时间为 ti。数据保证 1≤ai,bi≤n 且 0≤ti≤1000。接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj 和 vj,表示第 j 个运输计划是从 uj 号星球飞往 vj号星球。数据保证 1≤ui,vi≤n

输出描述 Output Description

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

样例输入 Sample Input

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

样例输出 Sample Output

11

数据范围及提示 Data Size & Hint

测试点编号 n= m= 约定
1 100 1  
2 100 100 第i条航道连接i号星球与i+1号星球
3 100 100  
4 2000 1
5 1000 1000 第i条航道连接i号星球与i+1号星球
6 2000 2000 第i条航道连接i号星球与i+1号星球
7 3000 3000 第i条航道连接i号星球与i+1号星球
8 1000 1000  
9 2000 2000
10 3000 3000
11 80000 1
12 100000 1
13 70000 70000

第i条航道连接i号星球与i+1号星球

14 80000 80000 第i条航道连接i号星球与i+1号星球
15 90000 90000 第i条航道连接i号星球与i+1号星球
16 100000 100000 第i条航道连接i号星球与i+1号星球
17 80000 80000  
18 90000 90000
19 100000 100000
20 300000 300000

所有数据

    1<=ai,bi,uj,vj<=n,0<=ti<=1000

之前的一些废话:还有一天出国,某年NOIP,由于我对NOIP的题都有心理阴影,所以一直不敢把这些没做的最后一题的坑填上,然而今天还是面对了这道题,并且能很快想出做法还是很开心的。

题解:首先蜜汁想到了二分(我也不知道为什么),二分的套路,判断当前解mid是否可行,如何判断是否可行呢?其实就是查看当前这些航线能否通过删去某一条对应的边使得每条路径和都小于mid即可。对于那些原来路径和就比mid小的航线就可以无视它们,只考虑那些需要进行裁剪的航线,我们现在需要找到一条边,使得所有比mid大的航线都必须经过这条边(要不然那些不过这条边的航线就无法进行剪裁,更不必说当前解可行了)。满足这种条件的边可能有很多,我们根据贪心的思路就选取最大的边,然后用路径和最大的那条航线减去这条边权(如果最大的那条航线都可以的话比他小的肯定都可以),并且判断这个结果与mid的大小关系,如果小于等于mid,那就说明当前解可行。

当然这只是一些想法,具体做法如下:首先我们通过dfs预处理出1到所有点的距离,这样方便后续航线路径和的计算,并把每一条航线进行结构体捆绑,按照路径和进行从小到大的排序。之后开始二分,上界是最大航线的路径和,下界是0,判断当前解是否可行的话用upper_bound在有序的航线中查出比mid大的第一条航线,然后开始考虑这些路径,需要找到一条边过所有航线,这个可以拿树上差分在O(n)时间内完成,最后扫一遍所有的边,判断tag[i]是否等于总航线的数值,并取其中的最大值,然后用路径和最大的那条航线减去这条边权,并且判断这个结果与mid的大小关系,如果小于等于mid,那就说明当前解可行。在树上差分的时候需要求出lca,因为我不会写O(1)求lca的,所以会多一个log,但是我们在预处理所有航线的路径和的过程中就会算出lca的值,所以提前存好即可。复杂度O((n+logm)logn)

二分照样很坑爹,改过N次二分的写法还是觉得不够稳,最后决定放弃,用最原始的写法,毕竟这样保险:

代码:

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<queue>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
#define mem(a,b) memset(a,b,sizeof(a))
inline int read()
{
int x=,f=;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-;c=getchar();}
while(isdigit(c)){x=x*+c-'';c=getchar();}
return x*f;
}
const int maxn=,maxm=;
int n,m,a,b,c,ce=-,es,top[maxn],first[maxn],deep[maxn],fa[maxn],size[maxn],dis[maxn],ms[maxn],L,R,mid;
int tag[maxn],maxw,w[maxn];
struct Edge
{
int u,v,w,next;
Edge() {}
Edge(int _1,int _2,int _3,int _4) :u(_1),v(_2),w(_3),next(_4) {}
}e[maxn<<];
struct path
{
int u,v,lc,len;
path() {}
path(int _1,int _2,int _3,int _4):u(_1),v(_2),lc(_3),len(_4) {}
bool operator < (const path& s)const {return len<s.len;}
}line[maxm];
void addEdge(int a,int b,int c)
{
e[++ce]=Edge(a,b,c,first[a]);first[a]=ce;
e[++ce]=Edge(b,a,c,first[b]);first[b]=ce;
}
void dfs(int now,int pa)
{
size[now]=;
for(int i=first[now];i!=-;i=e[i].next)
if(e[i].v!=pa)
{
deep[e[i].v]=deep[now]+;
dis[e[i].v]=dis[now]+e[i].w;
w[e[i].v]=e[i].w;
dfs(e[i].v,now);
if(size[ms[now]]<size[e[i].v])ms[now]=e[i].v;
size[now]+=size[e[i].v];
fa[e[i].v]=now;
}
}
void divide(int now,int chain)
{
top[now]=chain;
if(ms[now])divide(ms[now],chain);
for(int i=first[now];i!=-;i=e[i].next)if(e[i].v!=fa[now] && e[i].v!=ms[now])divide(e[i].v,e[i].v);
}
int lca(int a,int b)
{
while(top[a]!=top[b])
{
if(deep[top[a]]<deep[top[b]])swap(a,b);
a=fa[top[a]];
}
return deep[a]<deep[b] ? a : b;
}
void pushup(int now)
{
for(int i=first[now];i!=-;i=e[i].next)
if(fa[now]!=e[i].v)pushup(e[i].v),tag[now]+=tag[e[i].v];
}
bool check(int mid)
{
int maxw=;
path tmp=path(,,,mid);
int pos=upper_bound(line,line+m,tmp)-line;
mem(tag,);
for(int i=pos;i<m;i++)
{
tag[line[i].u]++;tag[line[i].v]++;
tag[line[i].lc]-=;
}
pushup();
for(int i=;i<=n;i++)if(tag[i]==m-pos)maxw=max(maxw,w[i]);
return line[m-].len-maxw<=mid;
}
int main()
{
mem(first,-);
n=read();m=read();
for(int i=;i<n;i++)a=read(),b=read(),c=read(),addEdge(a,b,c);
dfs(,);divide(,);
for(int i=;i<m;i++)
{
a=read();b=read();c=lca(a,b);
line[i]=path(a,b,c,dis[a]+dis[b]-*dis[c]);
}
sort(line,line+m);
L=;R=line[m-].len;
while(R-L>)
{
mid=(L+R)>>;
if(check(mid))R=mid;
else L=mid;
}
if(check(L))printf("%d\n",L);
else printf("%d\n",R);
return ;
}

总结:树上差分真的太好使了!!二分还是老老实实按上面那么写吧。

[CODEVS4632][BZOJ4326]运输计划的更多相关文章

  1. bzoj4326 运输计划

    4326: NOIP2015 运输计划 Time Limit: 30 Sec  Memory Limit: 128 MB Description 公元 2044 年,人类进入了宇宙纪元.L 国有 n ...

  2. NOIP2015 D2T3 洛谷2680 BZOJ4326 运输计划 解题报告

    前言:个人认为这是历年NOIP中比较简单的最后一题了,因此将自己的思路与大家分享. 题目大意: 给一棵无根树,给出m条路径.允许将树上的一条边的权值改为0.求m条路径长度最大值的最小值.n,m< ...

  3. [BZOJ4326][codevs4632][codevs5440][UOJ#150][NOIP2015]运输计划

    [BZOJ4326][codevs4632][codevs5440][UOJ#150][NOIP2015]运输计划 试题描述 公元 2044 年,人类进入了宇宙纪元. L 国有 n 个星球,还有 n− ...

  4. 【bzoj4326】[NOIP2015]运输计划

    题目描述 公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球.小 P 掌管一家物流公司, 该 ...

  5. NOIP2015 运输计划(bzoj4326)

    4326: NOIP2015 运输计划 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 886  Solved: 574[Submit][Status] ...

  6. [bzoj4326][NOIP2015]运输计划

    Description 公元2044年,人类进入了宇宙纪元. 国有个星球,还有条双向航道,每条航道建立在两个星球之间,这条航道连通了国的所有星球. 小掌管一家物流公司,该公司有很多个运输计划,每个运输 ...

  7. 【BZOJ-4326】运输计划 树链剖分 + 树上差分 + 二分

    4326: NOIP2015 运输计划 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 703  Solved: 461[Submit][Status] ...

  8. BZOJ4326或洛谷2680 运输计划

    BZOJ原题链接 洛谷原题链接 用\(LCA\)初始化出所有运输计划的原始时间,因为答案有单调性,所以二分答案,然后考虑检验答案. 很容易想到将所有超出当前二分的答案的运输计划所经过的路径标记,在这些 ...

  9. 【bzoj4326】[NOIP2015]运输计划 二分答案+LCA

    题目描述 公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球.小 P 掌管一家物流公司, 该 ...

随机推荐

  1. x1

    //程序功能: //要求客户从键盘输入一个整数,判断其是奇是偶 #include <stdio.h> int main(){ int x; printf("输入一个整数:\n&q ...

  2. 图片转PDF

    目的:图片转pdf(image2pdf)依赖:fpdf.php 网址 为 http://fpdf.org/ 有文档和包 demo:step 1 : First download fpdf librar ...

  3. 九、Spring之BeanFactory源码分析(一)

    Spring之BeanFactory源码分析(一) ​ 注意:该随笔内容完全引自https://blog.csdn.net/u014634338/article/details/82865644,写的 ...

  4. 现代WEB前端的性能优化

    现代WEB前端的性能优化 前言:这只是一份学习笔记. 什么是WEB前端 潜在的优化点: DNS是否可以通过缓存减少DNS查询时间? 网络请求的过程走最近的网络环境? 相同的静态资源是否可以缓存? 能否 ...

  5. 坑爹的京东E卡(京东E卡的正确使用方式)

      前言    今年中秋公司发了200的京东E卡(下面简称礼品卡,京东简称jd)这让喜欢在jd自营购买商品的我很是开心,    兴致勃勃打开官网,当我选好商品准备结算时却发现礼品卡无法使用.    后 ...

  6. 【UOJ#386】【UNR#3】鸽子固定器(贪心)

    [UOJ#386][UNR#3]鸽子固定器(贪心) 题面 UOJ 题解 一个不难想到的暴力做法是把东西按照\(s\)排序,这样子我们枚举极大值和极小值,那么我们选择的一定是这一段之间\(v\)最大的那 ...

  7. 一致性hash应用-分表扩容

    之前给项目里的一个5000多万的表做了水平分表,暂时容量还够,用的根据id一致性hash分了32个表,每个表大概百来万数据.虽然还不需要扩容,但是准备写个demo后续如果需要扩容可以参考 hash方法 ...

  8. .NET Core工作流引擎(RoadFlow)多语言版发布

    经过两个月的辛苦努力.NET Core工作流引擎(RoadFlow)多语言版发布了,在原来只有一种简体中文语言的基础上增加了繁体中文和英文两种语言,还可以通过扩展增加任意语言包.至此RoadFlow工 ...

  9. Java学习——单元测试JUnit

    Java学习——单元测试JUnit 摘要:本文主要介绍了什么是单元测试以及怎么进行单元测试. 部分内容来自以下博客: https://www.cnblogs.com/wxisme/p/4779193. ...

  10. Java性能 -- 线程上下文切换

    线程数量 在并发程序中,并不是启动更多的线程就能让程序最大限度地并发执行 线程数量设置太小,会导致程序不能充分地利用系统资源 线程数量设置太大,可能带来资源的过度竞争,导致上下文切换,带来的额外的系统 ...