题意:给出一个有\(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 排除无用边&最小生成树的更多相关文章

  1. Codeforces 196E Opening Portals MST (看题解)

    Opening Portals 我们先考虑如果所有点都是特殊点, 那么就是对整个图求个MST. 想在如果不是所有点是特殊点的话, 我们能不能也 转换成求MST的问题呢? 相当于我们把特殊点扣出来, 然 ...

  2. AtCoder Grand Contest 1~10 做题小记

    原文链接https://www.cnblogs.com/zhouzhendong/p/AtCoder-Grand-Contest-from-1-to-10.html 考虑到博客内容较多,编辑不方便的情 ...

  3. HNOI2014做题笔记

    HNOI2014 世界树(虚树.倍增) \(\sum M \leq 3 \times 10^5\)虚树没得跑 对于所有重要点和它们的\(LCA\)建立虚树,然后计算出每一个虚树上的点被哪个重要点控制. ...

  4. DP【洛谷P1704】 寻找最优美做题曲线

    [洛谷P1704] 寻找最优美做题曲线 题目背景 nodgd是一个喜欢写程序的同学,前不久(好像还是有点久了)洛谷OJ横空出世,nodgd同学当然第一时间来到洛谷OJ刷题.于是发生了一系列有趣的事情, ...

  5. NOIP2016考前做题(口胡)记录

    NOIP以前可能会持续更新 写在前面 NOIP好像马上就要到了,感觉在校内训练里面经常被虐有一种要滚粗的感觉(雾.不管是普及组还是提高组,我都参加了好几年了,结果一个省一都没有,今年如果还没有的话感觉 ...

  6. UOJ 做题记录

    UOJ 做题记录 其实我这么弱> >根本不会做题呢> > #21. [UR #1]缩进优化 其实想想还是一道非常丝播的题目呢> > 直接对于每个缩进长度统计一遍就好 ...

  7. C语言程序设计做题笔记之C语言基础知识(下)

    C 语言是一种功能强大.简洁的计算机语言,通过它可以编写程序,指挥计算机完成指定的任务.我们可以利用C语言创建程序(即一组指令),并让计算机依指令行 事.并且C是相当灵活的,用于执行计算机程序能完成的 ...

  8. C语言程序设计做题笔记之C语言基础知识(上)

    C语言是一种功能强大.简洁的计算机语言,通过它可以编写程序,指挥计算机完成指定的任务.我们可以利用C语言创建程序(即一组指令),并让计算机依指令行事.并且C是相当灵活的,用于执行计算机程序能完成的几乎 ...

  9. 屏蔽Codeforces做题时的Problem tags提示

    当在Codeforces上做题的时,有时会无意撇到右侧的Problem tags边栏,但是原本并不希望能够看到它. 能否把它屏蔽了呢?答案是显然的,我们只需要加一段很短的CSS即可. span.tag ...

随机推荐

  1. uvm设计分析——tlm

    tlm模块,用来在不同模块之间实现实时通信,主要基于两个定义在通信双方的port类来实现. 两个port之间,通过connect函数,来拿到双方的class指针,进而调用对方的function. 但是 ...

  2. C语言---变量与函数

    一个C程序是由一个或多个程序模块组成的,每一个程序模块作为一个源程序文件,一个源程序文件是一个编译单元. 源程序文件分为库函数和用户自己定义的函数,以及有参函数.无参函数. 函数调用的过程: 1) 定 ...

  3. awk命令学习(1)

    awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大.简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各 ...

  4. vim 命令学习(基础篇)

    [1]三种模式 vi的三种模式:命令模式.末行模式.编辑模式. 三种模式相互切换逻辑与命令图: 1.命令模式是vi的默认模式(即每打开一个文件时的初始模式). 2.命令模式切换至末行模式,末行模式切换 ...

  5. orb slam2 双目摄像头

    主要参考了http://blog.csdn.net/awww797877/article/details/51171099这篇文章,其中需要添加的是:export ROS_PACKAGE_PATH=$ ...

  6. 孤立森林(Isolation Forest)

    前言随着机器学习近年来的流行,尤其是深度学习的火热.机器学习算法在很多领域的应用越来越普遍.最近,我在一家广告公司做广告点击反作弊算法研究工作.想到了异常检测算法,并且上网调研发现有一个算法非常火爆, ...

  7. maven的profile详解

    详细内容请见:https://www.cnblogs.com/wxgblogs/p/6696229.html Profile能让你为一个特殊的环境自定义一个特殊的构建:profile使得不同环境间构建 ...

  8. 转:[C# 开发技巧]如何防止程序多次运行

    转载自:http://www.cnblogs.com/zhili/p/OnlyInstance.html 一.引言 最近发现很多人在论坛中问到如何防止程序被多次运行的问题的,如: http://soc ...

  9. spring boot 概念

    最近新版本迭代,一直在弄框架替换和新技术实现的事儿. 本来想仔细介绍一下Spring Boot的各种东西,后来发现没啥写的,Spring Boot 说白了就是把你开发过程中用到的各种框架给你封装了一下 ...

  10. js中的children实时获取子元素

    先看下面一个小例子的结果 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&q ...