【做题】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 ...
随机推荐
- MySQL创建外键约束的报错Error : Can't create table '#sql-534_185' (errno: 150)
总得来说是因为两个表的字段类型不一致,例如: 两个字段的类型或大小不严格匹配,一个为tinyint,另一个为char:或一个为int(10)另一个为int(9)也是不行的,即使都为int(10),但一 ...
- [2]传奇3服务器源码分析一 LoginServer
留存 服务端下载地址: 点击这里
- JSP知识点
1.九大内置对象: request HttpServletRequest类的实例 response HttpServletResponse类的实例 out PrintWriter类的实例,用于把结果输 ...
- hdu 5126 cdq+Treap+BIT
这题说的是给了三维空间然后操作 寻求在 x1,y1,z1 x2, y2, z2; (x1<x2, y1<y2,z1<z2) 计算出在 以这两个端点为右下和左上端点的方体内的点的 ...
- 转自大神的KM想法
我第一次理解KM算法看到大神的讲解不胜感激这km挺神奇的接下来就见识一下这个大牛的吧 转自 http://blog.csdn.net/wuxinxiaohuangdou/article/details ...
- Memento Mori (二维前缀和 + 枚举剪枝)
枚举指的是枚举矩阵的上下界,然后根据p0, p1, p2的关系去找出另外的中间2个点.然后需要记忆化一些地方防止重复减少时间复杂度.这应该是最关键的一步优化时间,指的就是代码中to数组.然后就是子矩阵 ...
- 开源词袋模型DBow3原理&源码(二)ORB特征的保存和读取
util里提供了create_voc_step0用于批量生成features并保存,create_voc_step1读入features再生成聚类中心,比较适合大量语料库聚类中心的生成. 提取一张图的 ...
- jsp页面报错 javax.servlet cannot be resolved to a type
需要引入 Tomcat 中的两个 jar 包: servlet-api jsp-api.jar
- Django后端项目---- Rest Framework(2)
一.认证(补充的一个点) 认证请求头 #!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.views import API ...
- GoldenGate 12.3发布
新特性: oracle db1. 支持12.2 oracle db2. 支持微服务架构, 可以使用restful api 管理OGG3. Parallel replicat,性能比integrated ...