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 ...
随机推荐
- 如何在面试中介绍自己的项目经验(面向java改进版)
本人于3年前写的博文,如何在面试中介绍自己的项目经验,经过大家的捧场,陆续得到了将近7万个点击量,也得到了众多网站公众号的转载,不过自己感觉,这篇文章更多的是偏重于方法,没有具体给到Java方面相关的 ...
- #3使用html+css+js制作网页 番外篇 使用python flask 框架 (II)
#3使用html+css+js制作网页 番外篇 使用python flask 框架 II第二部 0. 本系列教程 1. 登录功能准备 a.python中操控mysql b. 安装数据库 c.安装mys ...
- log4net配置及使用
log4net简介 log4net库是Apache log4j框架在Microsoft.NET平台的实现,是一个帮助程序员将日志信息输出到各种目标(控制台.文件.数据库等)的工具. log4net详解 ...
- openstack octavia的实现与分析(一)openstack负载均衡的现状与发展以及lvs,Nginx,Haproxy三种负载均衡机制的基本架构和对比
[负载均衡] 大量用户发起请求的情况下,服务器负载过高,导致部分请求无法被响应或者及时响应. 负载均衡根据一定的算法将请求分发到不同的后端,保证所有的请求都可以被正常的下发并返回. [主流实现-LVS ...
- [C#] 老古董的 Microsoft Chart Controls 也可以进行数据预测
我要先声明,这篇文章介绍到的内容虽说不是不能用,但玩乐成分居多,大家看看就好,不要太认真. 1. Microsoft Chart Controls 中的 FinancialFormula 在上一篇文章 ...
- Linux下的upx命令学习
upx学习 今天我们来学习一款给应用加壳的软件,叫做upx(the Ultimate Packer for eXecutables) 首先我们先看下它**百科的释义: UPX (the Ultimat ...
- python virtualenv 基本使用
下载 pip install virtualenv 校验是否成功 virtualenv --version 使用 创建env环境 要写一个新项目,使用env先创建环境 cd xx\xx\xx\ # 进 ...
- JavaScript 获得当前日期+时间
//直接从项目中copy出来的,亲测可用.function getTodayTime(){ var date = new Date(); var seperator1 = "-"; ...
- Approach for Unsupervised Bug Report Summarization 无监督bug报告汇总方法
AUSUM: approach for unsupervised bug report summarization 1. Abstract 解决的bug被归类以便未来参考 缺点是还是需要手动的去细读很 ...
- 在Linux系统下限制指定目录的大小以及文件/文件夹数量
背景说明 在Linux操作系统下有时需要限制一个指定文件夹的大小和文件夹内可存储的文件数量,有可能是出于安全的考量或者定制化的配置,这里我们提供了一种方案:用dd创建一个空的img镜像,进行格式化的配 ...