CF600 div2 F.Cheap Robot(思维+最短路+最小瓶颈路)
最开始啃这题的时候我还是个不会$lca$的人,看代码看的没有一点头绪,现在趁着寒假补了很多关于图论的知识点,回头在看这题还是有很多值得学习的地方。
Solution 1 (offline):
原题解:
Sort edges by new weight. Add them progressively, maintaining connexity with DSU.
As soon as two endpoints of a query become connected, we should put current capacity (i.e. new weight of the last edge added) as answer for this query.
To effeciently detect this, we can put tokens on endpoints of each query, and each time we do union (of DSU), we make tokens go up to the parent. If we do union by rank, each token will move at most$O(logn)$times.
离线的做法,我看了好久,开始的时候不太理解,后来看某$OI$选手的启发式合并的博客,我才有点感觉了。
先把新边按权值排序,然后逐步加边,将端点进行启发式合并(实际上就是最小生成树,考虑MST中每条边对答案的贡献)
比如我先加入一条1->3的边,那么我就去找和1相关的查询又没有在3所在的集合中
如果有就记录答案,因为有的话说明查询的两点间路径经过1->3这条边,又由于树中两点路径唯一,边是从小到大加进来的,一旦联通当前边的权值就是答案
至于为什么不去找和3相关的查询有没有在1所在的集合中,是因为预处理查询的时候加入的点对是双向的(可以自己脑补下,不太好描述)
没有的话就合并查询,由于边的加入使得这两个点联通,那么对应的查询也应该合并。
那么该如何合并呢?当然是启发式合并,其实就是小的集合并到大的集合里面。其实怎么合并都可以,答案都是对的,但是会出现这种情况:

原因是合并采取的方式是vector数组push_back(),假如你每次都是把大集合push_back()到小集合里面,那么内存可能会爆掉
看下代码应该就知道了:
#include <bits/stdc++.h>
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 1e15
using namespace std;
const int maxn = 3e6+10;
int n, m, k, q;
int f[maxn], head[maxn], cnt;
ll dis[maxn], ans[maxn];
priority_queue<pair<ll, int>> que;
bool vis[maxn];
vector<pair<int,int>> tmp[maxn];
struct node1{
int to, next;
ll w;
}e[maxn<<1];
struct node2{
int u, v;
ll w;
bool operator < (const node2 &x) const{
return w<x.w;
}
}edge[maxn];
//不能进行路径压缩, 那样会破坏原有的信息
int find(int x){
return f[x]==x ? x : find(f[x]);
}
void add(int u, int v, ll w){
e[++cnt].to = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt;
}
void dijkstra(){
for(int i = 1; i <= k; i++)
dis[i] = 0, que.push({0, i});
for(int i = k+1; i <= n; i++) dis[i] = inf;
while(!que.empty()){
int u = que.top().second; que.pop();
if(vis[u]) continue; vis[u] = 1;
for(int i = head[u]; i; i = e[i].next)
if(dis[u]+e[i].w<dis[e[i].to]){
dis[e[i].to] = dis[u] + e[i].w;
que.push({-dis[e[i].to], e[i].to}); //默认优先级较大, 所以需要*(-1)
}
}
}
int main(){
scanf("%d%d%d%d", &n, &m, &k, &q);
for(int i = 1; i <= m; i++){
int u, v;
ll w;
scanf("%d%d%lld", &u, &v, &w);
add(u, v, w), add(v, u, w);
edge[i] = node2{u, v, w};
}
dijkstra();
for(int i = 1; i <= m; i++) edge[i].w += dis[edge[i].u]+dis[edge[i].v];
sort(edge+1, edge+1+m);
for(int i = 1; i <= q; i++){
int u, v;
scanf("%d%d", &u, &v);
tmp[u].pb({v, i}), tmp[v].pb({u, i});
}
for(int i = 1; i <= n; i++) f[i] = i;
for(int i = 1; i <= m; i++){
int t1 = find(edge[i].u), t2 = find(edge[i].v);
if(t1==t2) continue;
if(tmp[t1].size()>tmp[t2].size()) swap(t1, t2);
for(auto it:tmp[t1]){
//if(ans[it.fi]) continue;
if(find(it.fi)==t2) ans[it.se] = edge[i].w;
else tmp[t2].pb(it); //合并查询
}
f[t1] = t2;
}
for(int i = 1; i <= q; i++) printf("%lld\n", ans[i]);
}
Solution 2 (online):
在线做法这就比较直接,直接用倍增$lca$ / $Kurskal$生成树+$lca$求最小瓶颈路就行了,思路很清晰
正好也学了树链剖分,可以写写练下手
#include <bits/stdc++.h>
#define ll long long
#define inf 1e15
using namespace std;
const int maxn = 6e5+10;
int n, m, k, q, u, v, cost, t1, t2;
int head1[maxn], head2[maxn];
int f[maxn], dep[maxn], son[maxn], size[maxn], top[maxn];
ll w[maxn], dis[maxn];
priority_queue<pair<ll, int>> que;
bool vis[maxn];
struct edge{
int to, next;
ll w;
}e1[maxn<<1], e2[maxn];
struct node{
int u, v;
ll w;
bool operator < (const node &x) const{
return w < x.w;
}
}data[maxn];
//路径压缩
int find(int x){
return f[x]==x ? x: f[x]=find(f[x]);
}
void dijkstra(){
for(int i = 1; i <= k; i++)
dis[i] = 0, que.push({0, i});
for(int i = k+1; i <= n; i++) dis[i] = inf;
while(!que.empty()){
int u = que.top().second; que.pop();
if(vis[u]) continue; vis[u] = 1;
for(int i = head1[u]; i; i = e1[i].next)
if(dis[u]+e1[i].w<dis[e1[i].to]){
dis[e1[i].to] = dis[u] + e1[i].w;
que.push({-dis[e1[i].to], e1[i].to});
}
}
}
void add1(int u, int v, int w){
e1[++t1].next = head1[u];
e1[t1].to = v, e1[t1].w = w;
head1[u] = t1;
}
void add2(int u, int v){
e2[++t2].next = head2[u];
e2[t2].to = v;
head2[u] = t2;
}
void dfs1(int u, int fa){
size[u] = 1;
for(int i = head2[u]; i; i = e2[i].next){
int v = e2[i].to;
if(v==fa) continue;
dep[v] = dep[u] + 1, f[v] = u;
dfs1(v, u), size[u] += size[v];
if(size[v]>size[son[u]]) son[u] = v;
}
}
void dfs2(int u, int topf){
top[u] = topf;
if(son[u]) dfs2(son[u], topf);
for(int i = head2[u]; i; i = e2[i].next){
int v = e2[i].to;
if(v==son[u]||v==f[u]) continue;
dfs2(v, v);
}
}
int lca(int u, int v){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u, v);
u = f[top[u]];
}
return dep[u]<dep[v] ? u : v;
}
int main(){
scanf("%d%d%d%d", &n, &m, &k, &q);
for(int i = 1; i <= m; i++){
scanf("%d%d%d", &u, &v, &cost);
add1(u, v, cost), add1(v, u, cost);
data[i] = node{u, v, cost};
}
dijkstra();
for(int i = 1; i <= m; i++) data[i].w += dis[data[i].u]+dis[data[i].v];
sort(data+1, data+1+m);
for(int i = 1; i <= n; i++) f[i] = i;
int cnt = n;
for(int i = 1; i <= m; i++){
int fu = find(data[i].u), fv = find(data[i].v);
if(fu==fv) continue;
w[++cnt] = data[i].w;
f[cnt] = f[fu] = f[fv] = cnt;
add2(cnt, fu), add2(cnt, fv);
}
dfs1(cnt, 0), dfs2(cnt, cnt);
while(q--){
scanf("%d%d", &u, &v);
printf("%lld\n", w[lca(u, v)]);
}
}
CF600 div2 F.Cheap Robot(思维+最短路+最小瓶颈路)的更多相关文章
- 【UVA10816】Travel in Desert (最小瓶颈路+最短路)
UVA10816 Travel in Desert 题目大意 沙漠中有一些道路,每个道路有一个温度和距离,要求s,t两点间的一条路径,满足温度最大值最小,并且长度最短 输入格式 输入包含多组数据. 每 ...
- 【UVA 10816】 Travel in Desert (最小瓶颈树+最短路)
[题意] 有n个绿洲, m条道路,每条路上有一个温度,和一个路程长度,从绿洲s到绿洲t,求一条道路的最高温度尽量小, 如果有多条, 选一条总路程最短的. InputInput consists of ...
- UVA-10816 Travel in Desert (最小瓶颈最短路)
题目大意:给一张无向图,图中的每条边都有两个权值,长度d和热度r.找出从起点到终点的一条最大热度最小的路径,如果这样的路径有多条,选择一个最短的. 题目分析:如果只考虑最小的最大热度,那么本题就是一个 ...
- Codeforces #451 Div2 F
#451 Div2 F 题意 给出一个由数字组成的字符串,要求添加一个加号和等号,满足数字无前导 0 且等式成立. 分析 对于这种只有数字的字符串,可以快速计算某一区间的字符串变成数字后并取模的值,首 ...
- Codeforces #452 Div2 F
#452 Div2 F 题意 给出一个字符串, m 次操作,每次删除区间 \([l,r]\) 之间的字符 \(c\) ,输出最后得到的字符串. 分析 通过树状数组和二分,我们可以把给定的区间对应到在起 ...
- Codeforces #541 (Div2) - F. Asya And Kittens(并查集+链表)
Problem Codeforces #541 (Div2) - F. Asya And Kittens Time Limit: 2000 mSec Problem Description Inp ...
- cf 442 div2 F. Ann and Books(莫队算法)
cf 442 div2 F. Ann and Books(莫队算法) 题意: \(给出n和k,和a_i,sum_i表示前i个数的和,有q个查询[l,r]\) 每次查询区间\([l,r]内有多少对(i, ...
- Codeforces #442 Div2 F
#442 Div2 F 题意 给出一些包含两种类型(a, b)问题的问题册,每本问题册有一些题目,每次查询某一区间,问有多少子区间中 a 问题的数量等于 b 问题的数量加 \(k\) . 分析 令包含 ...
- POJ 2253 Frogger【最短路变形/最小生成树的最大权/最小瓶颈树/A到B多条路径中的最小的最长边】
Freddy Frog is sitting on a stone in the middle of a lake. Suddenly he notices Fiona Frog who is sit ...
随机推荐
- 在mapper.xml映射文件中添加中文注释报错
问题描述: 在写mapper.xml文件时,想给操作数据库语句添加一些中文注释,添加后运行报如下错误: 思考 可能是写了中文注释,编译器在解析xml文件时,未能成功转码,从而导致乱码.但是文件开头也采 ...
- Linux下安装Oracle11g服务器【转】
安装环境 Linux服务器:oracle linux 6.6 64位 Oracle服务器:Oracle11gR2 64位 系统要求 Linux安装Oracle系统要求 系统要求 说明 内存 必须高于1 ...
- Ubuntu 18.04.4 LTS 更换国内系统源
Ubuntu 18.04.4 LTS 更换国内系统源 1.1) 好习惯先做备份在干活: mv /etc/apt/sources.list /etc/apt/sources.list.bak 1.2) ...
- ORA-00245 control file backup operation failed 分析和解决
一.问题说明 操作系统: RedHat 5.8 数据库: 11.2.0.3 2节点RAC. 使用RMAN 备份的时候,报如下错误: ORA-00245: control file backup fai ...
- VPS下环境漏洞部署
No.1 声明 1.由于本环节运行在公网,如何同样复现情况,复现成功后请立即关闭环境! 2.本环境仅用于漏洞复现! No.2 安装docker curl -s https://get.docker.c ...
- [Usaco2007 Jan]Telephone Lines架设电话线
题目描述 FarmerJohn打算将电话线引到自己的农场,但电信公司并不打算为他提供免费服务.于是,FJ必须为此向电信公司支付一定的费用.FJ的农场周围分布着N(1<=N<=1,000)根 ...
- Sklearn 与 TensorFlow 机器学习实战—一个完整的机器学习项目
本章中,你会假装作为被一家地产公司刚刚雇佣的数据科学家,完整地学习一个案例项目.下面是主要步骤: 项目概述. 获取数据. 发现并可视化数据,发现规律. 为机器学习算法准备数据. 选择模型,进行训练. ...
- 通过封装openpyxl模块实现自己的Excel操作类
""" excel类封装需要提供以下功能: 1.选择表单功能 2.读取一个单元格的数据功能 3.读取一行数据功能 4.读取表单中所有数据功能 5.往单元格中写入数据功能 ...
- 大型 web 前端架构设计-面向抽象编程入门
https://mp.weixin.qq.com/s/GG6AtBz6KgNwplpaNXfggQ 大型 web 前端架构设计-面向抽象编程入门 曾探 腾讯技术工程 2021-01-04 依赖反转 ...
- TCP报文段的首部格式 20字节的固定首部
<---- IP首部 TCP首部 TCP报文段的数据部分 <---- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ...