洛谷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 掌管一家 ...
随机推荐
- Linux路由器及交换机工作原理
IP包头中TTL字段的含义是什么?它用来做什么? TTL(time to live):该字段用于表示IP数据包的生命周期, 作用:限制一个数据在网络中无限循环的转发下去. 简述arp缓存表的建立过程: ...
- 徒手实现lower_bound和upper_bound
STL中lower_bound和upper_bound的使用方法:STL 二分查找 lower_bound: ; ; //初始化 l ,为第一个合法地址 ; //初始化 r , 地址的结束地址 int ...
- [LeetCode] 252. Meeting Rooms 会议室
Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2],...] (si ...
- Windows开机自动登陆 开/关:登录需按Ctrl+Alt+del的功能
Win10.Win7.WinXP开机自动登录: Win+R组合键打开“运行”,输入“netplwiz” 在弹出的“用户帐户”窗口中,把取消勾选“要使用本机,用户必须输入用户名和密码”,然后“应用”. ...
- 拒绝后门程序-Alibabaprotect和AliPaladin
详细参考帖子及评论区:流氓进程AlibabaProtect的删除[程序员吧]_百度贴吧 首先打开服务找到AlibabaProtect,然后找到他的位置(C:\Program Files (x86)\A ...
- win10 连接samba 账号密码不正确,win7可以访问
1.本地安全策略,本地策略-安全选项,需要修改成默认的值的修改方式: 查找注册表浏览到 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\LSA ...
- Scala 准引用 - Quasiquote介绍
Quasiquotes are a neat notation that lets you manipulate Scala syntax trees with ease: scala> val ...
- .NET Core应用中使用分布式缓存及内存缓存
.NET Core针对缓存提供了很好的支持 ,我们不仅可以选择将数据缓存在应用进程自身的内存中,还可以采用分布式的形式将缓存数据存储在一个“中心数据库”中.对于分布式缓存,.NET Core提供了针对 ...
- long 在不同操作系统下所占用的字节数
不同平台下C\C++数值数据类型长度如下: 类型 win32 win64 linux32 linux64 其中long类型和指针类型需要特别注意,编写跨平台的软件时尽量不要使用long类型,或者需要对 ...
- 深入理解C语言 - 指针详解
一.什么是指针 C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址.CPU 通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位.这里,数据对象是指存储在 ...