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 ...
随机推荐
- vs code编写java
不知不觉中vs code变得非常强大了,今天小编就分享一下vs code编写java语言.其实除了java语言,还支持很多语言. 首先看下vs code欢迎页面支持哪些语言: 好家伙,支持的东西还真不 ...
- Zap简单使用
前言 zap 是 uber 开源的一个日志记录的包, uber 在 go 的领域建树颇多, zap 更是优秀, 相比于自带的 log ,他有更多的功能, 当然, 最显眼的还是他很快, 本文介绍 zap ...
- vue 深度作用选择器
使用 scoped 后,父组件的样式将不会渗透到子组件中 如果想在使用scoped,不污染全局的情况下,依然可以修改子组件样式,可以使用深度作用选择器 .tree{ width: 100%; floa ...
- 【剑指 Offer】08.二叉树的下一个节点
题目描述 给定一颗二叉树和其中的一个节点,找出中序遍历序列的下一个节点.树中的节点除了有两个分别指向左右节点的指针,还有一个指向父节点的指针. Java public class Solution08 ...
- LeetCode145 二叉树的后序遍历
给定一个二叉树,返回它的 后序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [3,2,1] 进阶: 递归算法很简单,你可以通过迭代算法完成吗? /** * Defin ...
- LeetCode498 对角线遍历
给定一个含有 M x N 个元素的矩阵(M 行,N 列),请以对角线遍历的顺序返回这个矩阵中的所有元素,对角线遍历如下图所示. 示例: 输入: [ [ 1, 2, 3 ], [ 4, 5, 6 ], ...
- Linux学习笔记 | 配置ssh
目录: SSH的必要性 将默认镜像源修改为清华镜像源 Linux安装ssh软件 使用putty软件实现ssh连接 Windows下安装winscp SSH的必要性 一般服务器都位于远程而非本地,或者及 ...
- appium识别工具介绍
- 【win10】win10下两个显示器不同桌面壁纸
win10系统下,双屏显示为不同的桌面壁纸 操作: 1.鼠标右键点击个性化 2.点击背景选项 3.在图片上右键选择要添加为背景的图片 同理,将另一个屏幕壁纸设为监视器1 最后效果为两个分屏为不同桌面壁 ...
- 使用gui_upload的总结
今天使用gui_upload函数将文本文件的内容读取到内表.出现了一个问题,总是程序宕掉,出项的提示是 Type conflict when calling a function module. 原来 ...