题意:给出一个有\(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. ruby自动化之selenium webGUI

    1.下载ruby语言包,windows下需要安装rubyinstall http://railsinstaller.org/en 2.cmd命令下安装selenium-webdriver gem包 g ...

  2. 加快cin读取速度

    cin在读取大量数据时会比C里的scanf慢很多,但这并不是cin"无能",而是C++为了兼容C,对cin做了scanf的同步,只要关闭这个同步,cin就会有不弱于scanf的速度 ...

  3. 《大话设计模式》c++实现 工厂模式

    工厂模式 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在工厂模式中,我们在创建对象时不会对客户端 ...

  4. Rpgmakermv(30) GameQuest任务插件

    插件简介: 很牛X的任务插件(个人目前用过中的) 插件用法说明 Report any bugs, editor or plugin related here: http://forums.rpgmak ...

  5. 【CDH学习之一】CDH简介

    环境 虚拟机:VMware 10 Linux版本:CentOS-6.5-x86_64 客户端:Xshell4 FTP:Xftp4 jdk8 zookeeper-3.4.11 一.CDH在商业应用中,对 ...

  6. 类中静态成员变量 && 无法解析的外部符号

    [1]如下代码及编译错误 如标题,不做赘述. [2]原因及解决方案 原因:之所以报如上编译错误,因为静态成员变量未初始化. 解决方案:类中静态成员需要在类外进行初始化.其格式为:类型 类名::静态成员 ...

  7. python安装cv2

    pip install opencv-python

  8. selenium-grid 分布式 实现同一脚本在不同pc上运行

    1.使用版本:selenium 3.11.0chrome 65phantomjs 2.1.1selenium-server selenium-server-standalone-2.46.0.jar ...

  9. loadRunner手动关联, web_reg_save_param_regexp()函数正则匹配字符,赋值给变量

    loadRunner写脚本实现登录机票网站,手动关联,获取页面源码中特定字符 手动关联,就是通过函数获取某个步骤生成的字符,赋值给一个变量,这个变量可以作为接下来某个步骤的输入, 以便这个脚本能够在存 ...

  10. Linux基础命令---IP路由操作

    ip ip指令可以显示或操作路由.网路设备,设置路由策略和通道. 此命令的适用范围:RedHat.RHEL.Ubuntu.CentOS.SUSE.openSUSE.Fedora.   1.语法     ...