题目链接: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. 一文带你读懂什么是vxlan网络

    一个执着于技术的公众号 一.背景 随着云计算.虚拟化相关技术的发展,传统网络无法满足大规模.灵活性要求高的云数据中心的要求,于是便有了overlay网络的概念.overlay网络中被广泛应用的就是vx ...

  2. 【mq】从零开始实现 mq-06-消费者心跳检测 heartbeat

    前景回顾 [mq]从零开始实现 mq-01-生产者.消费者启动 [mq]从零开始实现 mq-02-如何实现生产者调用消费者? [mq]从零开始实现 mq-03-引入 broker 中间人 [mq]从零 ...

  3. 【多线程】线程休眠 Thread.sleep()

    线程休眠 Thread.sleep() sleep (时间) 指定当前线程阻塞的毫秒数: sleep存在异常InterruptedException: sleep时间达到后线程进入就绪状态: slee ...

  4. JS - 使用 html2canvas 将页面转PDF

    JS - 使用 html2canvas 将页面转PDF 本方法可以将页面元素块转为pdf. 网站地址 jspdf.js 官网地址:http://jspdf.com GitHub 主页:https:// ...

  5. Canvas 线性图形(五):多边形

    前言 CanvasRenderingContext2D 没有提供绘制多边形的函数,所以只能由我们自己来实现绘制多边形的函数.以六边形为基础,需要用到三角函数:sin 和 cos. 点 A 坐标 (一) ...

  6. 如何使用Superset可无缝对接MRS进行自助分析

    摘要:本文主要介绍如何在MRS之上使用Superset进行数据分析. 本文分享自华为云社区<使用商业智能软件Superset分析MRS数据之最佳实践>,作者: 啊喔YeYe . 1. 概要 ...

  7. script标签crossorigin属性及同源策略和跨域方法

    首先介绍(同源策略) 同源策略是浏览器最核心且基本的安全约定,要求协议.域名.端口都相同为同源,如果非同源时请求数据浏览器会在控制台抛出跨域异常错误,同源策略是浏览器的行为,即使客户端请求发送了,服务 ...

  8. 我的 Java 学习&面试网站又又又升级了!

    晚上好,我是 Guide. 距离上次介绍 JavaGuide 新版在线阅读网站已经过去 7 个多月了(相关阅读:官宣!我升级了!!!),这 7 个多月里不论是 JavaGuide 的内容,还是 Jav ...

  9. ExtJS 同行表单域对齐有误处理办法

    更新记录 2022年5月29日 第一次编辑.使用的ExtJS版本:ExtJS 7.4 问题 原本都是显示正常的表单域,比如这些文本框.选择框都是正常. 在用户进行操作,然后显示验证提示后,明显出现了问 ...

  10. Ubuntu Linux处理Waiting for cache lock: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 3365 (unattended-upgr)问题

    问题 在Ubuntu中,执行apt install后,出现以下问题: Waiting for cache lock: Could not get lock /var/lib/dpkg/lock-fro ...