题目链接:P2680 [NOIP2015 提高组] 运输计划 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

看了好长时间题解才终于懂的,有关lca和二分答案的题解解释的不详细,一时半会理解不过来,于是自己写一篇解释尽管解释主要在代码中,希望能对迷茫的小伙伴有帮助

解析(主要为二分答案的解析,lca只是求距离和找覆盖边时用得到,这里不多说):

由于m个运输计划是同时出发,所以所需要的时间取决于花费最长的时间,因为一个任务在y分钟内完成,那么另一个任务x(x<=y)也一定完成。

问题就转化成了求最长边最短,二分答案。

接下来,就是怎么二分了

想让时间大于答案的任务的时间减小,题目里说了,可以设一条边为虫洞,我们就找这个任务里哪条(些)边可以设为虫洞,这条(些)边就是被覆盖的边,我们将它们记录一下,所以我们只要找这些超时的任务的公共覆盖边就好了,即这条(些)边被记录的次数等于超时的任务数

代码有详细注释,不懂可以去看一下代码

#include<bits/stdc++.h>
#define ll long long
#define rll register long long
using namespace std;
const ll N=3e5+5;
ll n,m,cnt;
ll h[N],lg[N],sum[N],deep[N],st[N][20],vis[N],up[N];
struct edge//边
{
ll v,w,nxt;
} e[N<<1];
struct rw//任务
{
ll u,v,lca,dis;
bool operator <(const rw x)//重载运算符
{
return dis<x.dis;
}
} g[N];
inline ll read()
{
ll x=0;
bool flag=false;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') flag=true;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return flag?~x+1:x;
}
void add(ll u,ll v,ll w)
{
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].nxt=h[u];
h[u]=cnt;
}
void dfs(ll u,ll fat)
{
deep[u]=deep[fat]+1;//深度更新
st[u][0]=fat;//更新st表
for(rll i=1;i<=lg[deep[u]];++i)
{
st[u][i]=st[st[u][i-1]][i-1];//更新st表
}
for(rll i=h[u];i;i=e[i].nxt)//遍历
{
ll v=e[i].v,w=e[i].w;
if(v!=fat)//如果不是父节点
{
sum[v]=(sum[u]+w);
//到根节点的距离就是父节点到根节点的距离加上这条边的边权
up[v]=w;//处理到父节点的距离
dfs(v,u);//继续搜索
}
}
}
ll LCA(ll x,ll y)//正常求LCA
{
if(deep[x]<deep[y])
{
x=x^y,y=x^y,x=x^y;//位运算版本的交换
}
while(deep[x]>deep[y])
{
x=st[x][lg[deep[x]-deep[y]]-1];//让两点在同一深度
}
if(x==y) return x;
for(rll i=lg[deep[x]]-1;i>=0;--i)//找交汇之前最后到达的点
{
if(st[x][i]!=st[y][i])//如果不相交,向上跳
{
x=st[x][i];
y=st[y][i];
}
}
return st[x][0];//返回lca
}
bool check(ll len)
{
if(g[m].dis<=len) return true;
//如果路程最长的都比不过当前二分的答案,那么这个答案是可行的,直接返回
for(rll i=1;i<=n;++i) vis[i]=0;//每次操作前,数组清0
ll cont=0,maxn=0;
//cont 记录比当前答案大的边,maxn记录被覆盖次数最多的边被覆盖的次数
//(我知道maxn这里我解释的很trouble,不好理解就把他看成每条边被覆盖的次数的最大值)
//一定要时刻记住maxn的含义!!!
for(rll i=m;i>=1;--i)//遍历
{
if(g[i].dis<=len) break;
//如果当前任务的路程已经小于答案,就没必要继续遍历查找了,退出即可
cont++;//当前任务路程比答案大,计数器+1
ll u=g[i].u,v=g[i].v,lca=g[i].lca,c=g[i].dis-len;
//c 如果这条边大于或等于c,就说明删去它,这个任务的时间就小于或等于答案
//即这条边可以设为虫洞
while(u!=lca)//如果起点不是lca,那就向上找,查询被覆盖的边
{
if(up[u]>=c)//大于c,说明在这个任务中,它可以设为虫洞,也就是上文说的覆盖
{
vis[u]++;
maxn=max(maxn,vis[u]);//更新maxn
}
u=st[u][0];//往上慢慢找,时间限制是1s~2s
}
while(v!=lca)//如果终点不是lca,向上找
{
if(up[v]>=c)//和上面一样
{
vis[v]++;
maxn=max(maxn,vis[v]);//更新maxn
}
v=st[v][0];//慢慢跳(~ ̄▽ ̄)~
}
if(maxn<cont)return false;
//这里maxn只能小于等于cont,因为进行了cont次循环,一条边最多被覆盖cont次
//maxn是边被覆盖的次数的最大值
//如果小于cont,就说明找不到一条边可以被删去后使任务的时间小于等于答案
//所以这个答案不成立
//如果maxn==cont 就说明至少有一条边可以做到删去后使任务的时间小于等于答案
}
return true;
}
int main()
{
n=read(),m=read();
for(rll i=1;i<n;++i)
{
ll x=read(),y=read(),z=read();
add(x,y,z);
add(y,x,z);
}
for(rll i=1;i<=n;++i)
{
lg[i]=lg[i-1]+(1<<lg[i-1]==i);//预处理log,节约时间
}
dfs(1,0);//搜索
for(rll i=1;i<=m;++i)//记录每一个计划的信息
{
ll u=read(),v=read(),lca=LCA(u,v);
g[i].u=u;//起点
g[i].v=v;//终点
g[i].lca=lca;//lca
g[i].dis=sum[u]+sum[v]-(sum[lca]<<1);//这个计划的长度
}
sort(g+1,g+m+1);//将任务从小到大排序
ll l=0,r=3e8,ans;
while(l<r)
//注意!!!这里二分的是最终答案,也就是最小花费时间,清楚概念,否则后边就和我一样容易混
{
ll mid=(l+r)>>1;//二分操作
if(check(mid)) r=mid,ans=mid;
else l=mid+1;
}
printf("%lld\n",ans);
return 0;
}

[NOIP2015 提高组] 运输计划题解的更多相关文章

  1. NOIP2015 提高组] 运输计划

    码农题啊兄弟们. 随便考虑二分一下,然后发现要取一条满足性质的边. 被所有大于\(mid\)的路径都覆盖,取了之后能把他们都弄到小于\(mid\) 那就树上差分再处理一下. 写了\(180h\),老年 ...

  2. [NOIP2015提高组]运输计划

    题目:BZOJ4326.洛谷P2680.Vijos P1983.UOJ#150.codevs4632.codevs5440. 题目大意:有一棵带权树,有一些运输计划,第i个运输计划从ai到bi,耗时为 ...

  3. 洛谷 P2680 [NOIP2015 提高组] 运输计划

    链接:P2680 题意: 在树上把一条边边权变为0使得最长给定路径最短 分析: 最大值最小可以想到二分答案,对于每一个mid,寻找所有大于mid的路径,再寻找是否存在一条边使得删去它后大于mid的路径 ...

  4. P2680 [NOIP2015 提高组] 运输计划 (树上差分-边差分)

    P2680 题目的大意就是走完m条路径所需要的最短时间(边权是时间), 其中我们可以把一条边的权值变成0(也就是题目所说的虫洞). 可以考虑二分答案x,找到一条边,使得所有大于x的路径都经过这条边(差 ...

  5. 【数据结构】运输计划 NOIP2015提高组D2T3

    [数据结构]运输计划 NOIP2015提高组D2T3 >>>>题目 [题目描述] 公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n−1 条双向航道,每条航 ...

  6. 【题解】NOIP2015提高组 复赛

    [题解]NOIP2015提高组 复赛 传送门: 神奇的幻方 \([P2615]\) 信息传递 \([P2661]\) 斗地主 \([P2668]\) 跳石头 \([P2678]\) 子串 \([P26 ...

  7. 运输计划(题解)(Noip2015)

    运输计划(题解)(Noip2015) 二分答案+树上差分 树上差分其实不难,只是名字高大尚,可以学一下:Eternal风度的树上差分 本人博客里也总结了一些其他的知识供大家学习:Eternal风度的博 ...

  8. 洛谷 P2678 & [NOIP2015提高组] 跳石头

    题目链接 https://www.luogu.org/problemnew/show/P2678 题目背景 一年一度的“跳石头”比赛又要开始了! 题目描述 这项比赛将在一条笔直的河道中进行,河道中分布 ...

  9. 【二分查找】 跳石头NOIP2015提高组 D2T1

    [二分查找]跳石头NOIP2015提高组 D2T1 >>>>题目 [题目描述] 一年一度的“跳石头”比赛又要开始了! 这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石 ...

随机推荐

  1. 拖动元素调换位置——sortable.js

    使用简介: https://github.com/SortableJS/Sortable https://segmentfault.com/a/1190000008209715 /**! * Sort ...

  2. CSS Diner详解

    详细CSS Diner CSS Diner想必很多人听过,这是一个练习CSS中的选择器的不错的网站,最近在学习前端,打算好好写一下 常用英文单词: plates:盘子 bento:盒饭.便当 pick ...

  3. 好客租房18-jsx阶段总结

    JSX 1jsx是react的核心内容 2jsx是在js代码中写HTML结构,是react中声明式的提现 3使用jsx配合嵌入的js表达式,条件渲染,列表渲染,可以描述任意ui结构 4推荐使用cals ...

  4. python基础与数据类型(int, float, str, list)

    目录 python多版本共存 在cmd窗口进入不同版本的python环境 在pycharm中切换不同的版本 python语法之注释 python变量与常量 变量 变量的本质 变量的命名规范 常量 py ...

  5. typora的下载和基本的使用

    目录 typora的下载和基本的使用 typora的下载 typora基本的使用 选择自己喜爱的主题 创建标题 进入编程环境 改变文本样式 插入链接 插入图片 有序列表 无序列表 创建表格 单选框 表 ...

  6. 论文阅读 Dynamic Network Embedding by Modeling Triadic Closure Process

    3 Dynamic Network Embedding by Modeling Triadic Closure Process link:https://scholar.google.com.sg/s ...

  7. C#/VB.NET 在PDF表格中添加条形码

    条码的应用已深入生活和工作的方方面面.在处理条码时,常需要和各种文档格式相结合.当需要在文档中插入.编辑或者删除条码时,可借助于一些专业的类库工具来实现.本文,以操作PDF文件为例,介绍如何在编辑表格 ...

  8. npm错误:Cannot find module ‘compression-webpack-plugin

    转自 (82条消息) 前端开发遇到Cannot find module 'compression-webpack-plugin'问题解决_brave_zhao的博客-CSDN博客 <div id ...

  9. Git使用 - 忽略特定文件 - gitignore

    1. 背景 2. 创建.gitignore 文件 3. 文件内容样式 4. exclude文件 5. gitignore 文件模板 6. 参考文档 1. 背景 前提知识:在工作目录下的每一个文件都不外 ...

  10. 前端工作中用到的openlayers相关的公共方法

    /** * 获取地图上的图层对象 * @param map 地图对象 * @param layerName 实例化图层时的name * @return {null}*/ getLayerByLayer ...