## [$>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;
}

Codeforces 196 E. Tricky and Cleve Password的更多相关文章

  1. Codeforces 30 E. Tricky and Cleve Password

    \(>Codeforces \space 30\ E. Tricky\ and\ Cleve\ Password<\) 题目大意 : 给出一个串 \(S\),让你找出 \(A, B, C\ ...

  2. 算法训练 Tricky and Clever Password

     算法训练 Tricky and Clever Password   时间限制:2.0s   内存限制:256.0MB      问题描述 在年轻的时候,我们故事中的英雄——国王 Copa——他的私人 ...

  3. [Codeforces #196] Tutorial

    Link: Codeforces #196 传送门 A: 枚举 #include <bits/stdc++.h> using namespace std; #define X first ...

  4. 【Codeforces 429D】 Tricky Function

    [题目链接] http://codeforces.com/problemset/problem/429/D [算法] 令Si = A1 + A2 + ... + Ai(A的前缀和) 则g(i,j) = ...

  5. 【codeforces 429D】Tricky Function

    [题目链接]:http://codeforces.com/problemset/problem/429/D [题意] 给你n个数字; 让你求出一段区间[l,r] 使得 (r−l)2+(∑rl+1a[i ...

  6. Codeforces 196 C. Paint Tree

    分治.选最左上的点分给根.剩下的极角排序后递归 C. Paint Tree time limit per test 2 seconds memory limit per test 256 megaby ...

  7. 算法笔记_055:蓝桥杯练习 Tricky and Clever Password (Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 在年轻的时候,我们故事中的英雄——国王 Copa——他的私人数据并不是完全安全地隐蔽.对他来说是,这不可接受的.因此,他发明了一种密码,好 ...

  8. Codeforces 196 D. The Next Good String

    D. The Next Good String time limit per test 2 seconds memory limit per test 256 megabytes input stan ...

  9. [CF30E]Tricky and Clever Password(KMP+manacher)

    首先枚举回文中心,然后显然中心两边要尽量扩展作为middle,这个用manacher实现. 然后注意到suffix的结尾位置是固定的(串尾),那么预处理出以每个位置结尾的串与原串后缀至多能匹配多长,然 ...

随机推荐

  1. LintCode 508: Wiggle Sort

    LintCode 508: Wiggle Sort 题目描述 给你一个没有排序的数组,请将原数组就地重新排列满足如下性质 nums[0] <= nums[1] >= nums[2] < ...

  2. img图片居中

    关键词:clear: both;    display: block;    margin:auto; 图片居左,居右,居中: /* Alignment */ .alignleft { display ...

  3. xp,win7,win10系统安装GHO镜像下载

    淘宝买的纯净版系统镜像 不含任何垃圾软件 极致纯净 链接:http://pan.baidu.com/s/1eR12db0 密码:opjm 链接:http://pan.baidu.com/s/1mhEN ...

  4. WordPress手机端插件——WPtouch

    戒微博之后,把更多的精力开始转投回网站上来:今天用nexus7访问@Bee君 的博客时,发现博客的界面与电脑上访问的界面不相同,顺藤摸瓜之后发现原来bee君使用的是WPtouch-pro插件来实现移动 ...

  5. VueJS 集成 medium editor 自定义编辑器按钮

    详见我的新博客: 守望之吻

  6. sqlite3_get_table()

    { sqlite3 *db; char *errmsg=NULL;    //用来存储错误信息字符串 char ret=0; int my_age=0;    //类型根据要提取的数据类型而定 cha ...

  7. Nginx源码分析--epoll模块

    Nginx采用epoll模块实现高并发的网络编程,现在对Nginx的epoll模块进行分析. 定义在src/event/modules/ngx_epoll_module.c中 1. epoll_cre ...

  8. 2、gitlab 新建项目

    一.创建项目 1.访问gitlab并登录 http://git.xh.com/ 2.点击 Projects -> Starred projects 每个版本的gitlab不太一样但位置都差不多 ...

  9. PlantUML——2.中文乱码及解决

    关于中文 参见: http://plantuml.sourceforge.net/unicode.html   问题描述 第一步:创建demo03.txt描述文档: @startuml Alice - ...

  10. sql函数应用例子

    select p.province, data.existUserCount, data.addUserCount, data.cancelUserCount, data.threedayCancel ...