【做题】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 ...
随机推荐
- 删除SQL Server大容量日志的方法(转)
删除SQL Server大容量日志的方法 亲自实践的方法 1.分享数据库,如果提示被其他连接占用,不能分离,刚勾上drop connections 2.复制下所有文件,一定要备份好,以防自己操作失误 ...
- Unity shader学习之Alpha Blend
通过 ShaderLab 的 AlphaBlend 能够实现真正的半透明效果. 使用 AlphaBlend 时,需要关闭深度写入 ZWrite Off,但得非常小心物体的渲染顺序, RenderQue ...
- hdu5032 树状数组
题意: 对于一个1000*1000的Mushroom, 起点在(1,1)给定一个斜率和一个x,求由斜率和x所对应的直线构成的三角形内蘑菇的总值. 每个点的对应的值为(x+A)(y+B) 解 每个点都有 ...
- 基于TCP/IP协议的socket通讯client
package com.ra.car.utils; import java.io.BufferedReader; import java.io.IOException; import java.io. ...
- <keep-alvie></keep-alive>
<keep-alive></keep-alive>的作用是什么? <keep-alive></keep-alive> 包裹动态组件时,会缓存不活动的组件 ...
- svn安装使用
SVN安装使用 获取项目 1.首先新建文件夹.如:测试项目. 2.接着鼠标右键选择:SVN Checkout/SVN 检出 3.在出行的对话框中输入仓库地址.如:svn://198.021.262/2 ...
- 复习loadRunner参数化
参数化: 为什么要用参数化? 如果是单一数据,那么会纯测试缓存. 如果是参数化,基本上大部分数据不会被缓存命中. 极端情况:所有的数据都不会被缓存命中,或者少量命中. 在loadrunner中,所有的 ...
- Linux基础命令---ifdown、ifup
ifup ifup指令用来启动网络接口设备,设备必须是定义在“/etc/sysconfig/network-scripts/ifcfg-ethX”或者“/etc/sysconfig/network”的 ...
- 进程表示之进程ID号
UNIX进程总是会分配一个号码用于在其命名空间总唯一地标识它们,该号码称作进程ID号,简称PID. 1.进程ID 但每个进程除了PID外,还有其他的ID,有下列几种可能的类型: (1)处于某个线程组中 ...
- 细数那些不懂Spring底层原理带来的伤与痛
1. 什么是spring? Spring 是个Java企业级应用的开源开发框架.Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用.Spring 框架目标是简化Jav ...