Description

给定 \(n\) 个点,每个点有点权,连结两个点花费的代价为两点的点权和。另外有 \(m\) 条特殊边,参数为 \(x,y,z\)。意为如果你选择这条边,就可以花费 \(z\) 的代价将点 \(x\) 和点 \(y\) 连结起来,当然你也可以不选择这条边。求使整个图联通的最小代价

Input

第一行是两个整数,分别是点数 \(n\) 和特殊边的数量 \(m\)

下面一行 \(n\) 个数,第 \(i\) 个数代表点 \(i\) 的点权

下面 \(m\) 行,每行三个数 \(x,y,z\),代表一条特殊边

Output

输出一行一个整数,最小代价

Hint

\(1~\leq~n~\leq~2~\times~10^5~,0~\leq~m~\leq~2~\times~10^5\)

设第 \(i\) 个点的点权为 \(a_i\),第 \(j\) 条特殊边的边权为 \(z_j\),保证

\(1~\leq~a_i,z_j~\leq~10^{12}\)

保证特殊边的参数 \(x~\neq~y\)

Solution

显然这个题目是要求一个 MST (最小生成树),但是 \(O(n^2)\) 建图显然不可取。

我们考虑克鲁斯卡尔算法的本质:

有若干个联通块,每次寻找联通块间权值最小的边将之连结。

考虑对于本题来说,在不考虑特殊边的情况下,连结两个联通块,显然要分别选择两个联通块内点权最小的点连结。于是我们对每个集合维护点权最小的点,每次取出点权前两小的集合进行连边即可。维护点权前两小的集合显然可以用一个堆做。

考虑有特殊边怎么办:

我们把特殊边排序,每次比较当前的特殊边的权值小还是前两个联通块的点权小,选择更小的合并。

注意处理一下当前选出的两个点在一个集合中的情况。

因为一共要做 \(O(n)\) 次,每次会有 \(O(1)\) 次对堆的插入和删除操作,于是复杂度 \(O(n \log n)\)

Code

#include <cstdio>
#include <queue>
#include <algorithm>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#endif
#define rg register
#define ci const int
#define cl const long long typedef long long int ll; namespace IPT {
const int L = 1000000;
char buf[L], *front=buf, *end=buf;
char GetChar() {
if (front == end) {
end = buf + fread(front = buf, 1, L, stdin);
if (front == end) return -1;
}
return *(front++);
}
} template <typename T>
inline void qr(T &x) {
rg char ch = IPT::GetChar(), lst = ' ';
while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
if (lst == '-') x = -x;
} template <typename T>
inline void ReadDb(T &x) {
rg char ch = IPT::GetChar(), lst = ' ';
while ((ch > '9') || (ch < '0')) lst = ch, ch = IPT::GetChar();
while ((ch >= '0') && (ch <= '9')) x = x * 10 + (ch ^ 48), ch = IPT::GetChar();
if (ch == '.') {
ch = IPT::GetChar();
double base = 1;
while ((ch >= '0') && (ch <= '9')) x += (ch ^ 48) * ((base *= 0.1)), ch = IPT::GetChar();
}
if (lst == '-') x = -x;
} namespace OPT {
char buf[120];
} template <typename T>
inline void qw(T x, const char aft, const bool pt) {
if (x < 0) {x = -x, putchar('-');}
rg int top=0;
do {OPT::buf[++top] = x % 10 + '0';} while (x /= 10);
while (top) putchar(OPT::buf[top--]);
if (pt) putchar(aft);
} const int maxn = 200010;
const int maxm = 400010; struct Edge {
int from, to;
ll v;
inline bool operator<(const Edge &_others) const {
return this->v < _others.v;
}
};
Edge edge[maxm]; int n, m;
int ufs[maxn], vec[maxn], rk[maxn];
ll ans;
ll MU[maxn]; struct Zay {
int id;
ll v;
inline bool operator<(const Zay &_others) const {
return this->v > _others.v;
}
};
std::priority_queue<Zay>Q; int find(ci x) {return ufs[x] == x ? x : ufs[x] = find(ufs[x]);} int main() {
freopen("1.in", "r", stdin);
qr(n); qr(m);
for (rg int i = 1; i <= n; ++i) qr(MU[i]);
for (rg int i = 1; i <= m; ++i) {
qr(edge[i].from); qr(edge[i].to); qr(edge[i].v);
}
std::sort(edge + 1, edge + 1 + m);
edge[m + 1].v = 1ll << 62;
for (rg int i = 1; i <= n; ++i) ufs[i] = i, vec[i] = i, rk[i] = 1, Q.push((Zay){i, MU[i]});
for (rg int i = 1, cur = 1; i < n; ++i) {
while ((cur <= m) && (find(edge[cur].from) == find(edge[cur].to))) ++cur;
Zay t1 = Q.top(); Q.pop(); Zay t2 = Q.top(); Q.pop();
while (find(t1.id) == find(t2.id)) {t2 = Q.top(); Q.pop();}
if ((t1.v + t2.v) <= edge[cur].v) {
int fa = find(t1.id), fb = find(t2.id);
ans += t1.v + t2.v;
if (rk[fa] < rk[fb]) ufs[fb] = fa;
else if (rk[fa] > rk[fb]) ufs[fa] = fb;
else ufs[fa] = fb, ++rk[fb];
Q.push((Zay){find(fa), t1.v});
vec[find(fa)] = vec[fa];
} else {
int fa = find(edge[cur].from), fb = find(edge[cur].to);
ans += edge[cur].v;
if (rk[fa] < rk[fb]) ufs[fb] = fa;
else if (rk[fa] > rk[fb]) ufs[fa] = fb;
else ufs[fa] = fb, ++rk[fb];
Q.push((Zay){find(fa), MU[vec[fa]]});
vec[find(fa)] = vec[fa];
Q.push(t1); Q.push(t2);
}
}
qw(ans, '\n', true);
return 0;
}

【生成树,堆】【CF1095F】 Make It Connected的更多相关文章

  1. [CF1095F]Make It Connected

    题目大意:给你$n(n\leqslant2\times10^5)$个点和$m(m\leqslant2\times10^5)$条边,第$i$个点点权为$a_i$.连接$u,v$两个点的代价为$a_u+a ...

  2. 【CF1095F】 Make It Connected(最小生成树)

    题目链接 如果没有特殊边的话显然答案就是权值最小的点向其他所有点连边. 所以把特殊边和权值最小的点向其他点连的边丢一起跑最小生成树就行了. #include <cstdio> #inclu ...

  3. 题解 CF1095F 【Make It Connected】

    题意简述 \(n\)( \(1≤n≤2×10^5\) )个点,每个点 \(i\) 有一个点权 \(a_i\) ( \(1≤a_i≤2×10^{12}\) ),将两个点 \(i\),\(j\) 直接相连 ...

  4. POJ 2728 Desert King 最优比率生成树

    Desert King Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 20978   Accepted: 5898 [Des ...

  5. POJ 2728 Desert King(最优比例生成树 二分 | Dinkelbach迭代法)

    Desert King Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 25310   Accepted: 7022 Desc ...

  6. [POJ2728] Desert King 解题报告(最优比率生成树)

    题目描述: David the Great has just become the king of a desert country. To win the respect of his people ...

  7. PAT树_层序遍历叶节点、中序建树后序输出、AVL树的根、二叉树路径存在性判定、奇妙的完全二叉搜索树、最小堆路径、文件路由

    03-树1. List Leaves (25) Given a tree, you are supposed to list all the leaves in the order of top do ...

  8. ACM:Pseudoforest-并查集-最大生成树-解题报

    Pseudoforest Time Limit:5000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u Submit Status ...

  9. hdu 3367 Pseudoforest(最大生成树)

    Pseudoforest Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) To ...

随机推荐

  1. selenium 各种很奇葩的异常

    问题1:使用selenium3+java的脚本模拟登陆时,总是提示用户名,密码错误 解决方法:1 在执行输入用户名和密码的代码之前,加上driver.navigate().refresh(); QQ群 ...

  2. C/C++语言基础

    1. 一个子类中含有其他类对象,则构造函数的顺序是? 先执行基类的(如果基类当中有虚基类,要先执行虚基类的,其他基类则按照声明派生类是的顺序依次执行),在执行成员对象的,最后执行自己的. 2.spri ...

  3. 斯坦福大学机器学习(Andrew Ng@2014)--自学笔记

    今天学习Andrew NG老师<机器学习>之6 - 6 - Advanced Optimization,做笔记如下: 用fminunc函数求代价函数最小值,分两步: 1.自定义代价函数 f ...

  4. Paper Reading - Im2Text: Describing Images Using 1 Million Captioned Photographs ( NIPS 2011 )

    Link of the Paper: http://papers.nips.cc/paper/4470-im2text-describing-images-using-1-million-captio ...

  5. World Cup(思维+模拟)

    Description Allen wants to enter a fan zone(球迷区) that occupies a round square and has nn entrances. ...

  6. 王者荣耀交流协会scrum立会20171111

    1.立会照片 成员王超,高远博,冉华,王磊,王玉玲,任思佳,袁玥全部到齐. master:高远博 2.时间跨度: 2017年11月10日 18:00 - 18:33 ,总计33分钟. 3.地 点: 一 ...

  7. Xftp安装和使用的视频录制方法

    内容: 1.使用工具 2.操作步骤及方法 视频地址: http://v.youku.com/v_show/id_XMzEwNjg2MTg2NA==.html?spm=a2h3j.8428770.341 ...

  8. 寻找bug

    bug1:void不应有返回值. bug2:while(n--)没有条件终止循环. bug3:size和data没有定义 bug4:arr 是sz 在大于0的情况下创建的 一定部位bull   下面的 ...

  9. C++:默认初始化

    一.什么是默认初始化 默认初始化,顾名思义,即为在定义变量时如果没有为其指定初始化值,则该变量会被C++编译器赋予默认的值.而变量被赋予的默认值到底是什么,则取决于变量的数据类型和变量的定义位置. 二 ...

  10. 四则运算4 WEB(结对开发)

    在第三次实验的基础上,teacher又对此提出了新的要求,实现网页版或安卓的四则运算. 结对开发的伙伴: 博客名:Mr.缪 姓名:缪金敏 链接:http://www.cnblogs.com/miaoj ...