## [$>Codeforces \space 196\ E. Tricky\ and\ Cleve\ Password

题目大意 : 给出一个有 \(n\) 个结点,\(m\) 条边的连通带权无向图,有k个点设有传送门。开启的传送门可以花费 \(0\) 的代价传送,走一条边要话费等同边权的代价, 一开始,所有传送门关闭, 每当你到达一个有传送门的点,那个传送门就会永久开启, 求从 \(1\) 号点出发开启所有传送门所需的最小代价

\(1 \leq n, m \leq 10^5\)

解题思路 :

为了简化表述,下文把具有传送门的点称为关键点,用 \(dis(x, y)\) 表示点 \(x, y\) 之间的最短路径长度,\((x, y)\) 表示 \(x, y\) 之间的边

观察发现,答案的形态是每次从一个已经打开的关键点出发沿着最短路径走到一个未打开的关键点

那么答案可以看成从 \(1\) 号点出发,走到一个最近的关键点,然后关键点之间做\(MST\), 两两之间的边权是两点之间的最短路

考虑直接对这个答案形态做暴力,建一个新的完全图跑\(MST\),边数最坏是 \(n^2\) 级别,复杂度可以达到 \(O(n^2logn)\)

观察发现答案的形态有些冗余,不妨减去没用的状态. 考虑新图上的每一条边对应原图的一条路径,也就是原图中的若干条边

反过来想,原图上的每一条边都有可能对应新图上的一条路径,也就是对应一条连接两个关键点的边

观察发现,对于原图上的边 \((x, y)\) ,设 \(p_x, p_y\) 为离 \(x\) 最近的关键点和离 \(y\) 最近的关键点,其对应的路径就是 \(p_x \rightarrow (x, y) \rightarrow p_y\) ,那么这条路径在新图上的边权就是 \(dis(p_x, x) +(x,y)+dis(y, p_y)\)

证明:假设存在一条关键点之间最短路径 \(c_x \rightarrow c_y\) ,$\forall\ (x, y) \in (c_x \rightarrow c_y) $ 满足\(\ p_x \neq c_x\) 或 \(p_x \neq c_y\) 或 \(p_y \neq c_x\) 或 \(p_y \neq c_y\)

那么对于路径上每一条边 \((x, y)\) 都存在 \(dis(p_x, x) + (x, y) + dis(y, p_y) \leq dis(c_x, x) + (x, y) + dis(y, p_y)\)

也就是说,这条路径对应的新边是其所在环上的最大边,根据 \(Kruskal\) 的环切性质,这条边一定不在 \(MST\) 中

至于为什么一定在环上嘛,别忘了新图是一个完全图 \(QwQ\)

所以这样连边保证了不会遗漏在 \(MST\) 上的边,同时边数变成了 \(O(m)\) 级别,总复杂度是 \(O((m+n)logm)\)

所以只需要一遍 \(Dijkstra\) 预处理出最短路和新图的边权,一遍 \(Kruskal\) 求 \(MST\) 即可巧妙的解决此题

/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf ((ll)(1e18))
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int f = 0, ch = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
#define int ll const int N = 500005; int a[N], b[N], head[N], nxt[N], cnt;
int dis[N], g[N], s[N], fa[N], n, m, k; struct Edge{ int x, y, z; } e[N];
inline bool cmp(Edge A, Edge B){ return A.z < B.z; } inline void add(int x, int y, int z){
a[++cnt] = y, b[cnt] = z, nxt[cnt] = head[x], head[x] = cnt;
} struct Node{
int d, id;
bool operator < (const Node &A) const{ return d > A.d; }
}; priority_queue<Node> pq; inline void Dijkstra(){
for(int i = 1; i <= n; i++) dis[i] = inf / 3;
for(int i = 1; i <= k; i++)
dis[s[i]] = 0, g[s[i]] = i, pq.push((Node){0, s[i]});
while(!pq.empty()){
Node now = pq.top(); pq.pop();
int u = now.id;
if(now.d != dis[u]) continue;
for(int p = head[u]; p; p = nxt[p]){
int v = a[p];
if(dis[u] + b[p] < dis[v]){
dis[v] = dis[u] + b[p];
g[v] = g[u], pq.push((Node){dis[v], v});
}
}
}
} inline int ask(int x){
return x == fa[x] ? x : fa[x] = ask(fa[x]);
}
inline int Kruskal(){
int ans = 0;
for(int i = 1; i <= n; i++) fa[i] = i;
sort(e + 1, e + m + 1, cmp);
for(int i = 1; i <= m; i++){
int x = e[i].x, y = e[i].y, z = e[i].z;
int p = ask(x), q = ask(y);
if(p != q) fa[p] = q, ans += z;
}
return ans;
} signed main(){
read(n), read(m);
for(int i = 1, x, y, z; i <= m; i++){
read(x), read(y), read(z);
add(x, y, z), add(y, x, z);
e[i] = (Edge){x, y, z};
}
read(k);
for(int i = 1; i <= k; i++) read(s[i]);
Dijkstra();
for(int i = 1; i <= m; i++){
e[i].z += dis[e[i].x] + dis[e[i].y];
e[i].x = g[e[i].x], e[i].y = g[e[i].y];
}
cout << Kruskal() + dis[1] << endl;
return 0;
}

最新文章

  1. 使用ajaxfileupload.js实现文件上传
  2. 关于WCF服务在高并发情况下报目标积极拒绝的异常处理
  3. git恢复误删文件及省去密码提交
  4. Settings.System.getInt获取Setting里的设置信息
  5. linux下的mysql乱码问题
  6. php header函数要点
  7. Python 基础【第六篇】字典
  8. 小学生之JAVA中的分层
  9. XML新手入门 创建构造良好的XML(2)
  10. 2017,科学使用strace神器(附代码,举栗子)
  11. [js高手之路]打造通用的匀速运动框架
  12. 将文件内容转化为byte数组返回
  13. 一个简单的定向python爬虫爬取指定页面的jpg图片
  14. Could not load file or assembly (Exception from HRESULT: 0x80131047)-解决办法
  15. KNN-笔记(2)
  16. git操作手册
  17. Ubuntu14.04设置开机启动脚本(转)
  18. 质因子分解(Pollard_Rho法)
  19. SpringBoot开发项目,引入JPA找不到findOne方法
  20. Jsp的语法和指令

热门文章

  1. js中字符串和json数组的相互转换
  2. Vue 子路由 与 单页面多路由 的区别
  3. [HDU1205]吃糖果 题解(鸽巢原理)
  4. 2017ACM暑期多校联合训练 - Team 7 1002 HDU 6121 Build a tree (深搜+思维)
  5. bzoj 2653 二分答案+可持久化线段树
  6. 重载jquery on方法实现click事件在移动端的快速响应
  7. python3学习笔记.1.初体验
  8. php的发展历史
  9. SPI协议及其工作原理浅析【转】
  10. 健身VS不健身,完全是两种不同的人生!