洛谷P2680 运输计划(倍增LCA + 树上差分 + 二分答案)
【思路】:
根据题意可以明显看出,当所有任务都完成时的时间是最终的结果,也就是说本题要求,求出最小的最大值。
那这样的话就暗示了将答案二分,进行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 + 树上差分 + 二分答案)的更多相关文章
- 洛谷 P2680 运输计划-二分+树上差分(边权覆盖)
P2680 运输计划 题目背景 公元 20442044 年,人类进入了宇宙纪元. 题目描述 公元20442044 年,人类进入了宇宙纪元. L 国有 nn 个星球,还有 n-1n−1 条双向航道,每条 ...
- 洛谷 P2680 运输计划 解题报告
P2680 运输计划 题目背景 公元2044年,人类进入了宇宙纪元. 题目描述 公元2044年,人类进入了宇宙纪元. \(L\)国有\(n\)个星球,还有\(n-1\)条双向航道,每条航道建立在两个星 ...
- [NOIP2015] 提高组 洛谷P2680 运输计划
题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球. 小 P 掌管一家 ...
- 洛谷P2680 运输计划 [LCA,树上差分,二分答案]
题目传送门 运输计划 Description 公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n?1 条双向航道,每条航道建立在两个星球之间, 这 n?1 条航道连通了 L 国的所 ...
- 洛谷P2680 运输计划——树上差分
题目:https://www.luogu.org/problemnew/show/P2680 久违地1A了好高兴啊! 首先,要最大值最小,很容易想到二分: 判断当前的 mid 是否可行,需要看看有没有 ...
- 洛谷P2680 运输计划(树上差分+二分)
传送门 考虑树上乱搞 首先这是满足二分性质的,如果在某个时间可以完成工作那么比他更长的时间肯定也能完成工作 然后考虑二分,设当前答案为$mid$,如果有一条链的长度大于$mid$,那么这条链上必须得删 ...
- 洛谷 P2680 运输计划(NOIP2015提高组)(BZOJ4326)
题目背景 公元 \(2044\) 年,人类进入了宇宙纪元. 题目描述 公元\(2044\) 年,人类进入了宇宙纪元. L 国有 \(n\) 个星球,还有 \(n-1\) 条双向航道,每条航道建立在两个 ...
- 洛谷——P2680 运输计划
https://www.luogu.org/problem/show?pid=2680 题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每 ...
- 洛谷 P2680 运输计划
题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球. 小 P 掌管一家 ...
随机推荐
- CF1136E Nastya Hasn't Written a Legend(线段树)
还能说什么呢,简直太妙了. $$a_{i+1}<a_i+k_i$$ $$a_{i+1}-k_i-k_{i-1}-\cdots-k_1<a_i+k_i-k_i-k_{i-1}-\cdots- ...
- [LeetCode] 57. Insert Interval 插入区间
Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessa ...
- swagger案例Swagger案例
pom <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework ...
- 聊聊Runloop
1.什么是Runloop 在开始聊RunLoop之前,我们先来了解一下程序的执行原理.一般来说,程序是在线程中执行,一个线程一次只能执行一个任务(关于GCD,可看上篇文章介绍),执行完成后线程就会退出 ...
- Hystrix(服务熔断,服务降级)
一.Hystrix 1.服务雪崩 多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C有调用其他的微服务,这就是所谓的”扇出”,如扇出的链路上某个微服务的调用响应式过长或者 ...
- windows10安装ubuntu双系统教程(初稿)
windows10安装ubuntu双系统教程(绝对史上最详细) Win10 Ubuntu16.04/Ubuntu18.04双系统完美安装 Windows10+Ubuntu18.04双系统安装成功心得( ...
- linux中断子系统
参考引用:http://www.wowotech.net/sort/irq_subsystem wowotech:一个很好的linux技术博客. 一.概述 目的 kernel管理硬件设备的方式:轮询. ...
- c++小学期大作业攻略(二)整体思路+主界面
写在前面:如果我曾经说过要在第一周之内写完大作业,那……肯定是你听错了.不过如果我在写的时候有攻略看的话应该可以轻松地在4~5天内做完,然后觉得写攻略的人是个小天使吧(疯狂暗示).出于给大家自由发挥的 ...
- ReentrantReadWriteLock可重入,锁升级,锁降级
public class ReentrantReadWriteLockTest { public static void main(String[] args) throws InterruptedE ...
- Shiro 使用 JWT Token 配置类参考
项目中使用了 Shiro 进行验证和授权,下面是 Shiro 配置类给予参考. 后来并没有使用 Shiro,感觉使用 JWT 还是自己写拦截器比较灵活,使用 Shiro 后各种地方需要魔改,虽然功能也 ...