最开始啃这题的时候我还是个不会$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(思维+最短路+最小瓶颈路)的更多相关文章

  1. 【UVA10816】Travel in Desert (最小瓶颈路+最短路)

    UVA10816 Travel in Desert 题目大意 沙漠中有一些道路,每个道路有一个温度和距离,要求s,t两点间的一条路径,满足温度最大值最小,并且长度最短 输入格式 输入包含多组数据. 每 ...

  2. 【UVA 10816】 Travel in Desert (最小瓶颈树+最短路)

    [题意] 有n个绿洲, m条道路,每条路上有一个温度,和一个路程长度,从绿洲s到绿洲t,求一条道路的最高温度尽量小, 如果有多条, 选一条总路程最短的. InputInput consists of ...

  3. UVA-10816 Travel in Desert (最小瓶颈最短路)

    题目大意:给一张无向图,图中的每条边都有两个权值,长度d和热度r.找出从起点到终点的一条最大热度最小的路径,如果这样的路径有多条,选择一个最短的. 题目分析:如果只考虑最小的最大热度,那么本题就是一个 ...

  4. Codeforces #451 Div2 F

    #451 Div2 F 题意 给出一个由数字组成的字符串,要求添加一个加号和等号,满足数字无前导 0 且等式成立. 分析 对于这种只有数字的字符串,可以快速计算某一区间的字符串变成数字后并取模的值,首 ...

  5. Codeforces #452 Div2 F

    #452 Div2 F 题意 给出一个字符串, m 次操作,每次删除区间 \([l,r]\) 之间的字符 \(c\) ,输出最后得到的字符串. 分析 通过树状数组和二分,我们可以把给定的区间对应到在起 ...

  6. Codeforces #541 (Div2) - F. Asya And Kittens(并查集+链表)

    Problem   Codeforces #541 (Div2) - F. Asya And Kittens Time Limit: 2000 mSec Problem Description Inp ...

  7. 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, ...

  8. Codeforces #442 Div2 F

    #442 Div2 F 题意 给出一些包含两种类型(a, b)问题的问题册,每本问题册有一些题目,每次查询某一区间,问有多少子区间中 a 问题的数量等于 b 问题的数量加 \(k\) . 分析 令包含 ...

  9. 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 ...

随机推荐

  1. 洛谷 P4396 [AHOI2013]作业

    题目描述 题目传送门 分析 因为询问是关于区间的,并且没有强制在线,所以能用莫队解决 但是还要支持查询区间内大于等于 \(a\),小于等于 \(b\) 的数的个数和数值的个数 所以还要套一个数据结构 ...

  2. 【MySQL】MySQL知识图谱

    MySQL 文章目录 MySQL 表 锁 索引 连接管理 事务 日志系统 简单记录 极客时间 - MySQL实战45讲 MySQL知识图谱 表 表 引擎选择 编码问题 表空间管理 字段设计 备份和恢复 ...

  3. 【Oracle】修改oracle中SGA区的大小

    1.备份数据库: 2.关机,拔下电源和各种连接线,抽出机箱,打开机箱上盖,增加内存: 3.完成后按原样将各个部件及连接线恢复好,电开机,系统正常运行: 4.进入系统查看,发现内存已经顺利安装: 5.修 ...

  4. kubernets之ReplicaSet

    一   介绍RS 1.1   RS与RC在功能上基本上是一摸一样的,因为两者的功能都是用来管控集群内部的pod,并且 两者都具备模版,副本数量以及标签选择器等三要素,区别点在于,RS拥有着更为强大的标 ...

  5. os.system('cmd')在linux和windows系统下返回值的差异

    今天,用os.system('cmd')分别在windows和linux平台上执行同一ping命令,命令执行失败时返回码不同,windows为1,而linux下返回为256,如下: linux下: & ...

  6. awk中引用shell变量的方法

    1.通过命令行参数定义变量时引用: awk -v awk变量名= shell变量名 #!/bin/bash var4bash=test awk -v var4awk="$var4bash&q ...

  7. ALV中的分隔条(SPLITTER_CONTROL)

    如上图,可以做成左右的分割,当然也可以做成上下的分割效果,在每个分割的容器内,显示各自的内容. 需要使用的class: cl_gui_splitter_container, cl_gui_custom ...

  8. Sentry(v20.12.1) K8S 云原生架构探索,JavaScript 性能监控之采样 Transactions

    系列 Sentry-Go SDK 中文实践指南 一起来刷 Sentry For Go 官方文档之 Enriching Events Snuba:Sentry 新的搜索基础设施(基于 ClickHous ...

  9. 编译Nacos,解决No Server available 以及 failed to req API__nacos_v1_ns_instance after all servers

    问题描述:如图,显示没有服务可用 仔细看控制台,看到上面Error部分,相关参数没有读取到配置信息,那么配置信息这块似乎是有问题,赶紧看看IDE对配置信息的扫描情况: 可以看到有信息了,但是报错:No ...

  10. Golang 单元测试:有哪些误区和实践?

    https://mp.weixin.qq.com/s/k8WNWpCIVl4xTmP3TQ_gxQ