【做题】CF196E. Opening Portals 排除无用边&最小生成树
题意:给出一个有\(n\)个结点,\(m\)条边的连通无向图,边有边权,等于经过这条边所需的时间。有\(k\)个点设有传送门。一开始,所有传送门关闭。你从\(1\)号点出发,每当你到达一个有传送门的点,那个传送门就会永久开启。你可以从一个开启的传送门花费\(0\)时间到达另一个开启的传送门。求开启所有传送门所需的最小时间。
\(n,m \leq 10^5\)
首先,简化一下题目。当到达第一个传送门点后,因为已经过传送门可以互相传送,所以剩下的时间就是所有传送门点两两之间连权值等于最短路的边建成的完全图的最小生成树。因此答案就是那个最小生成树,再加上\(1\)号点到最近的传送门的距离。
因此,我们得到了一个\(O(n^2 \log n)\)的算法。
接下来,问题就变成了如何快速求最小生成树。不难想到,如果传送门点\(u\)到\(v\)的最短路上有其他传送门点,那么\((u,v)\)一定不在MST上。遗憾的是,单单这么剪枝是没有用的,边数还是会被下图所示的菊花卡成\(O(n^2)\)。

但是,这个失败的尝试启示我们要剪去一些不可能在最小生成树上的边。考虑kruskal的过程,一条边只要在一个其他边都“小于”它的环上(指边权小于或边权等于但枚举顺序靠前),就一定不在最小生成树上。
我们难以直接判断这个条件,但是,对于一条最短路径,如果它上面的每一条边两端最近的传送门点都不是这条路径的两个端点,就可以排除了。因此,我们构建一张新图\(G'\),对于原图上每一条边\((u,v)\),设与\(u\)最近的任意一个传送门点为\(nex_u\),与\(v\)最近的是\(nex_v\),那么,在新图上建一条\((nex_u,nex_v)\)的边,边权是原边权加上\(dis(nex_u,u) + dis(nex_v,v)\)。这相当于考虑所有边所在的最短路,并且,所有没有被考虑到的最短路一定是可以删去的。这样,我们就把边数剪到了\(O(n)\)级别。
时间复杂度\(O(n \log n)\)。
#include <bits/stdc++.h>
using namespace std;
#define gc() getchar()
template <typename tp>
inline void read(tp& x) {
char tmp; bool key = 0;
x = 0;
for (tmp = gc() ; !(tmp >= '0' && tmp <= '9') ; tmp = gc())
key = (tmp == '-');
for ( ; tmp >= '0' && tmp <= '9' ; tmp = gc())
x = (x << 3) + (x << 1) + tmp - '0';
if (key) x = -x;
}
// begin here
typedef long long ll;
const int N = 100010;
const ll INF = 1ll << 60;
struct edge {
int la,b,v;
} con[N << 1];
int tot,fir[N];
void add(int from,int to,int val) {
con[++tot] = (edge) {fir[from],to,val};
fir[from] = tot;
}
int n,m,vis[N],num,nex[N],uni[N];
ll dis[N],ans = INF;
struct data {
int a,b;
ll v;
bool operator < (const data& x) const {
return v < x.v;
}
} dat[N];
typedef pair<ll,int> pii;
struct cmp {
bool operator () (const pii& a,const pii& b) {
return a > b;
}
};
priority_queue<pii,vector<pii>,cmp> q;
vector<int> st;
void dijkstra() {
while (!q.empty()) q.pop();
memset(dis,0x3f,sizeof dis);
memset(vis,0,sizeof vis);
for (int i = 0 ; i < (int)st.size() ; ++ i) {
dis[st[i]] = 0;
nex[st[i]] = st[i];
q.push(pii(dis[st[i]],st[i]));
}
for (int pos ; !q.empty() ; ) {
pos = q.top().second;
q.pop();
if (vis[pos]) continue;
vis[pos] = 1;
for (int i = fir[pos] ; i ; i = con[i].la) {
if (dis[con[i].b] > dis[pos] + con[i].v) {
dis[con[i].b] = dis[pos] + con[i].v;
nex[con[i].b] = nex[pos];
q.push(pii(dis[con[i].b],con[i].b));
}
}
}
}
int getfa(int pos) {
return pos == uni[pos] ? pos : uni[pos] = getfa(uni[pos]);
}
int main() {
int x,y,z;
read(n), read(m);
for (int i = 1 ; i <= m ; ++ i) {
read(x), read(y), read(z);
dat[i] = (data) {x,y,z};
add(x,y,z);
add(y,x,z);
}
st.clear();
st.push_back(1);
dijkstra();
st.clear();
read(num);
for (int i = 1 ; i <= num ; ++ i) {
read(x);
ans = min(ans,dis[x]);
st.push_back(x);
}
dijkstra();
for (int i = 1 ; i <= n ; ++ i)
uni[i] = i;
for (int i = 1 ; i <= m ; ++ i) {
dat[i].v += dis[dat[i].a] + dis[dat[i].b];
dat[i].a = nex[dat[i].a];
dat[i].b = nex[dat[i].b];
}
sort(dat+1,dat+m+1);
for (int i = 1 ; i <= m ; ++ i) {
x = dat[i].a, y = dat[i].b;
x = getfa(x), y = getfa(y);
if (x == y) continue;
uni[x] = y;
ans += dat[i].v;
}
cout << ans << endl;
return 0;
}
小结:这类知识点简单的题目还是很有思维难度的。这一点,还要弥补。
【做题】CF196E. Opening Portals 排除无用边&最小生成树的更多相关文章
- Codeforces 196E Opening Portals MST (看题解)
Opening Portals 我们先考虑如果所有点都是特殊点, 那么就是对整个图求个MST. 想在如果不是所有点是特殊点的话, 我们能不能也 转换成求MST的问题呢? 相当于我们把特殊点扣出来, 然 ...
- AtCoder Grand Contest 1~10 做题小记
原文链接https://www.cnblogs.com/zhouzhendong/p/AtCoder-Grand-Contest-from-1-to-10.html 考虑到博客内容较多,编辑不方便的情 ...
- HNOI2014做题笔记
HNOI2014 世界树(虚树.倍增) \(\sum M \leq 3 \times 10^5\)虚树没得跑 对于所有重要点和它们的\(LCA\)建立虚树,然后计算出每一个虚树上的点被哪个重要点控制. ...
- DP【洛谷P1704】 寻找最优美做题曲线
[洛谷P1704] 寻找最优美做题曲线 题目背景 nodgd是一个喜欢写程序的同学,前不久(好像还是有点久了)洛谷OJ横空出世,nodgd同学当然第一时间来到洛谷OJ刷题.于是发生了一系列有趣的事情, ...
- NOIP2016考前做题(口胡)记录
NOIP以前可能会持续更新 写在前面 NOIP好像马上就要到了,感觉在校内训练里面经常被虐有一种要滚粗的感觉(雾.不管是普及组还是提高组,我都参加了好几年了,结果一个省一都没有,今年如果还没有的话感觉 ...
- UOJ 做题记录
UOJ 做题记录 其实我这么弱> >根本不会做题呢> > #21. [UR #1]缩进优化 其实想想还是一道非常丝播的题目呢> > 直接对于每个缩进长度统计一遍就好 ...
- C语言程序设计做题笔记之C语言基础知识(下)
C 语言是一种功能强大.简洁的计算机语言,通过它可以编写程序,指挥计算机完成指定的任务.我们可以利用C语言创建程序(即一组指令),并让计算机依指令行 事.并且C是相当灵活的,用于执行计算机程序能完成的 ...
- C语言程序设计做题笔记之C语言基础知识(上)
C语言是一种功能强大.简洁的计算机语言,通过它可以编写程序,指挥计算机完成指定的任务.我们可以利用C语言创建程序(即一组指令),并让计算机依指令行事.并且C是相当灵活的,用于执行计算机程序能完成的几乎 ...
- 屏蔽Codeforces做题时的Problem tags提示
当在Codeforces上做题的时,有时会无意撇到右侧的Problem tags边栏,但是原本并不希望能够看到它. 能否把它屏蔽了呢?答案是显然的,我们只需要加一段很短的CSS即可. span.tag ...
随机推荐
- 学习笔记<2>Android基本四大组件
<1>Activity(活动) ------与用户交互的界面 (1)Activity启动基本流程 <2>service(服务) <3>ContentProvider ...
- GeoJSON 和 TopoJSON
GeoJSON 和 TopoJSON 是符合 JSON 语法规则的两种数据格式,用于表示地理信息. 1. GeoJSON GeoJSON 是用于描述地理空间信息的数据格式.GeoJSON 不是一种新的 ...
- v1版本
<?php use yii\helpers\Html; use yii\helpers\Url; use yii\widgets\DetailView; use yii\grid\GridVie ...
- Latex数学公式中的空格
http://blog.sina.com.cn/s/blog_4ddef8f80100iwwv.html 两个quad空格 a \qquad b 两个m的宽度 quad空格 a \quad b 一个m ...
- 如何用新安装的jdk替换掉Linux系统默认jdk
版主写的非常棒: http://blog.csdn.net/u011364306/article/details/48375653
- usdt转入转出出入金开发
usdt转入转出出入金开发 比特币协议 -> Omni 层协议 -> USDTUSDT是基于比特币omni协议的一种代币: https://omniexplorer.info/asset/ ...
- windows下多个python版本共存,如何在Windows7系统上安装最新的64位Python3.6.2
windows下多个python版本共存,如何在Windows7系统上安装最新的64位Python3.6.2 1.官网下载python3.6.2https://www.python.org/ftp/p ...
- Javascript创建类的七种方法
/* 第一种定义类的方法 */var cls = new Object();cls.name = "wyf";cls.showName = function(){console.l ...
- System.map文件的作用解析
有关System.map文件的信息好象很缺乏.其实它一点也不神秘,并且在整个事情当中它并不象看上去那么得重要.但是由于缺乏必要的文档说明,使其显得比较神秘.它就象耳垂,我们每个人都有,但却不知道是干什 ...
- 细数那些不懂Spring底层原理带来的伤与痛
1. 什么是spring? Spring 是个Java企业级应用的开源开发框架.Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用.Spring 框架目标是简化Jav ...