【题目链接】

【思路】:

根据题意可以明显看出,当所有任务都完成时的时间是最终的结果,也就是说本题要求,求出最小的最大值。

那这样的话就暗示了将答案二分,进行check。

【check方法】:

如果说当前答案为ans,每个任务设为p[i],所花费的时间是p[i].tim,所有任务p[i].tim的最大值为maxdis

那么则将符合条件p[i].tim>=ans的数量num求出来,这个数量也就是符合条件的路径的数量(一个任务在u,v之间有一个简单路径很容易理解),

然后找到一个所有路径中他们的公共边(公共边就是这个边在符合条件的p[i]中出现的次数==num的边)中最大的一个mmax,如果说

maxdis-mmax<=ans那么check返回1,使得上界变成mid-1,理由是如果最长的路减去一个此时最大的公共边比此时答案ans小,说明ans还可以

继续减小,即让最大值变得更小,反之则不能。

【寻找公共边方法】:

使用树上差分,对任务进行离线处理,定义cnt[x]是x到x的父亲gra[x][0]这条边经过的次数,在check函数中,符合p[i].tim>ans的就让cnt[p[i].u] ++, cnt[p[i].v]++, cnt[p[i].lca] -= 2

所有的任务都判断完成时,进行Dfs更新所有节点的cnt[],当cnt[x]==num && dis[x]-dis[gra[x][0]]>=mmax时更新mmax。进行完Dfs后判断maxdis-mmax与ans关系即可。

#include <bits/stdc++.h>
using namespace std; const int maxn = 3e5 + ;
const int maxm = maxn;
const int inf = 0x3f3f3f3f;
int n, m, mmax, num, ans, maxdis;
struct edge{
int to, w, next;
} ed[maxn<<];
struct plan{
int u, v, lca, tim;
plan( int u=, int v=, int lca=, int tim= ):
u(u),v(v),lca(lca),tim(tim){}
} p[maxm];
int head[maxn], tot, cnt[maxn];
int gra[maxn][], dep[maxn], dis[maxn], maxdep;
inline int read(){
int k=0, f=1;
char ch=getchar();
while( ch>''|| ch<'' ){ if( ch=='-' ) f = -; ch = getchar(); }
while( ch<='' && ch>='' ){ k = k*+ch-''; ch = getchar(); }
return k*f;
} inline void init(){
memset( head ,- ,sizeof(head) );
memset( gra, , sizeof(gra) );
maxdep = log(n)/log();
tot = ;
} inline void add( int u, int v, int w ){
ed[++tot].to = v;
ed[tot].w = w;
ed[tot].next = head[u];
head[u] = tot;
} inline void dfs_lca( int x ){ //初始化与LCA相关的数据
for( int i=; i<=maxdep; i++ ){
gra[x][i] = gra[gra[x][i-]][i-];
if( !gra[x][i] ) break;
}
for( int i=head[x]; ~i; i=ed[i].next ){
int y = ed[i].to;
if( y==gra[x][] ) continue;
dep[y] = dep[x]+;
dis[y] = dis[x]+ed[i].w;
gra[y][] = x;
dfs_lca(y);
}
} inline void dfs_diff( int x ){
for( int i=head[x]; ~i; i=ed[i].next ){
int y = ed[i].to;
if(y==gra[x][]) continue;
dfs_diff(y);
cnt[x] += cnt[y];
}
if( cnt[x]==num && mmax<dis[x]-dis[gra[x][]] ) //寻找最大的公共边
mmax = dis[x]-dis[gra[x][]];
} inline void swap( int &a, int &b ){
int t = a;
a = b;
b = t;
} inline int LCA( int x, int y ){
if(dep[x]>dep[y]) swap(x, y);
for( int i=maxdep; ~i; i-- )
if( gra[y][i]==x ) return x; //避免卡常,能return就return
else if( dep[gra[y][i]]>=dep[x] ) y = gra[y][i];
if( x==y ) return x; //避免卡常
for( int i=maxdep; ~i; i-- )
if( gra[x][i]!=gra[y][i] ){
x = gra[x][i];
y = gra[y][i];
}
if( x!=y ) x = gra[x][];
return x;
} inline int max( int a, int b ){
return a>b ? a:b;
} inline bool check( int x ){
mmax = num = ; //每次check都将mmax, num, cnt初始化为0
memset( cnt ,, sizeof(cnt) );
for( int i=; i<=m; i++ ){
if( p[i].tim<=x ) continue;
num ++;
cnt[p[i].u] ++;
cnt[p[i].v] ++;
cnt[p[i].lca] -= ;
}
dfs_diff(); //更新每个结点的cnt
return maxdis-mmax<=x;
} int main(){
n = read(); m = read(); //读取方式使用快读,此题卡常数卡的很厉害
init();
int maxe = -inf;
for( int i=; i<n; i++ ){
int u, v, w;
u = read(); v = read(); w = read();
add(u, v, w);
add(v, u, w);
maxe = max( maxe, w );
}
dep[] = ;
dis[] = ;
dfs_lca();
maxdis = -inf;
for( int i=; i<=m ;i++ ){
int u, v;
u = read(); v = read();
int lca = LCA(u, v);
p[i] = plan( u, v, lca, dis[u]+dis[v]-(dis[lca]<<) ); //储存,后续进行离线处理
maxdis = max( maxdis, p[i].tim ); //获得一条边都不是虫洞的最大值
}
int l = maxdis-maxe, r = maxdis; //这里要优化下界l,不然会超时
while( l<=r ){
int mid = (l+r)>>;
if( check(mid) ){
ans = mid;
r = mid-;
}
else l = mid+;
}
printf("%d\n", ans); return ;
}

洛谷P2680 运输计划(倍增LCA + 树上差分 + 二分答案)的更多相关文章

  1. 洛谷 P2680 运输计划-二分+树上差分(边权覆盖)

    P2680 运输计划 题目背景 公元 20442044 年,人类进入了宇宙纪元. 题目描述 公元20442044 年,人类进入了宇宙纪元. L 国有 nn 个星球,还有 n-1n−1 条双向航道,每条 ...

  2. 洛谷 P2680 运输计划 解题报告

    P2680 运输计划 题目背景 公元2044年,人类进入了宇宙纪元. 题目描述 公元2044年,人类进入了宇宙纪元. \(L\)国有\(n\)个星球,还有\(n-1\)条双向航道,每条航道建立在两个星 ...

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

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

  4. 洛谷P2680 运输计划 [LCA,树上差分,二分答案]

    题目传送门 运输计划 Description 公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n?1 条双向航道,每条航道建立在两个星球之间, 这 n?1 条航道连通了 L 国的所 ...

  5. 洛谷P2680 运输计划——树上差分

    题目:https://www.luogu.org/problemnew/show/P2680 久违地1A了好高兴啊! 首先,要最大值最小,很容易想到二分: 判断当前的 mid 是否可行,需要看看有没有 ...

  6. 洛谷P2680 运输计划(树上差分+二分)

    传送门 考虑树上乱搞 首先这是满足二分性质的,如果在某个时间可以完成工作那么比他更长的时间肯定也能完成工作 然后考虑二分,设当前答案为$mid$,如果有一条链的长度大于$mid$,那么这条链上必须得删 ...

  7. 洛谷 P2680 运输计划(NOIP2015提高组)(BZOJ4326)

    题目背景 公元 \(2044\) 年,人类进入了宇宙纪元. 题目描述 公元\(2044\) 年,人类进入了宇宙纪元. L 国有 \(n\) 个星球,还有 \(n-1\) 条双向航道,每条航道建立在两个 ...

  8. 洛谷——P2680 运输计划

    https://www.luogu.org/problem/show?pid=2680 题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每 ...

  9. 洛谷 P2680 运输计划

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

随机推荐

  1. cf 1182 E - Product Oriented Recurrence

    当时脑残了, 不会写矩阵快速幂中更改的系数, 其实把他扔到矩阵里同时递推就好了 #include<cstdio> #include<algorithm> #include< ...

  2. 8.19 NOIP模拟测试26(B) 嚎叫响彻在贪婪的厂房+主仆见证了 Hobo 的离别+征途堆积出友情的永恒

    T1 嚎叫响彻在贪婪的厂房 以前做过一个等比数列的题「序列」,这个类似 是等差数列且公差不为1的条件就是各项差的绝对值的$gcd!=1$,每次拿出序列前两个数,求出差值,插入到set里,每次向后扩展, ...

  3. 8.10 NOIP模拟测试16 Blue+Weed+Drink

    T1 Blue 贪心,每次跳得时候跳能跳到的最远的地方,跳过的就把他设为0,每次二分找到位置,一直跳就行,如果能跳到的位置就是当前位置或比当前位置还小(数组里现在呆着的这一块石头,二分得到的就是当前位 ...

  4. [LeetCode] 337. House Robber III 打家劫舍之三

    The thief has found himself a new place for his thievery again. There is only one entrance to this a ...

  5. oracle--报错 ORA-01003,ORA-09817,ORA-01075

    磁盘满了,删除旧文件,即可保证登入成功

  6. Python 腾讯云短信,发送手机验证码

    1.安装包 pip install qcloudsms_py 2.准备好相关参数 腾讯云短信每个月赠送100条短信,申请一个应用,获取appid,然后创建短信签名,然后创建正文模版 3.发送短信 我们 ...

  7. Elasticsearch由浅入深(七)搜索引擎:_search含义、_multi-index搜索模式、分页搜索以及深分页性能问题、query string search语法以及_all metadata原理

    _search含义 _search查询返回结果数据含义分析 GET _search { , "timed_out": false, "_shards": { , ...

  8. Oracle性能调优之虚拟索引用法简介

    本博客记录一下Oracle虚拟索引的用法,虚拟索引是定义在数据字典中的伪索引,可以说是伪列,没有修改的索引字段的.虚拟索引的目的模拟索引,不会增加存储空间的使用,有了虚拟索引,开发者使用执行计划的时候 ...

  9. 使用excel中的数据快速生成sql语句

    在小公司的话,总是会有要开发去导入历史数据(数据从旧系统迁移到新系统上)的时候.这个时候,现场实施或客户会给你一份EXCEL文档,里面包含了一些别的系统上的历史数据,然后就让你导入到现在的系统上面去. ...

  10. 【05】Kubernets:资源清单(控制器 - ReplicaSet)

    写在前面的话 从上一章节开始,我们一直在学习关于自主式 Pod 的资源清单编写, 但是自主式 Pod 存在着一个问题,就是和我们直接 docker run 运行 docker 容器一样.如果我们想以集 ...