Solution CF1095F Make It Connected
题目大意:给定 (n) 个点,每个点有一个点权 (a_i),在 (i,j) 之间连边的代价为 (a_i + a_j)。除此之外,还给定了额外 (m) 条边。求最小代价使得原图联通。
贪心,最小生成树
分析:首先考虑 (m=0) 的做法
我们直接贪心,选取 (a) 最小的点作为中心,建出一个菊花图,这样一定是最优的。
证明考虑归纳法,类似于 Prim 算法的思想
对于 (n = 2) 的情况显然
对于 (n > 2),前 (n - 1) 个点已经形成了一棵生成树,我们就要把当前点接到树上。当前点的点权是不变的,我们肯定贪心去选树上点权最小的点。
这样找出一棵生成树之后,我们再把 (m) 条边加入,一起跑最小生成树就可以了
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
constexpr int maxn = 2e5 + 100;
typedef long long ll;
struct IO{//-std=c++11,with cstdio and cctype
private:
static constexpr int ibufsiz = 1 << 20;
char ibuf[ibufsiz + 1],*inow = ibuf,*ied = ibuf;
static constexpr int obufsiz = 1 << 20;
char obuf[obufsiz + 1],*onow = obuf;
const char *oed = obuf + obufsiz;
public:
inline char getchar(){
#ifndef ONLINE_JUDGE
return ::getchar();
#else
if(inow == ied){
ied = ibuf + sizeof(char) * fread(ibuf,sizeof(char),ibufsiz,stdin);
*ied = ' ';
inow = ibuf;
}
return *inow++;
#endif
}
template<typename T>
inline void read(T &x){
static bool flg;flg = 0;
x = 0;char c = getchar();
while(!isdigit(c))flg = c == '-' ? 1 : flg,c = getchar();
while(isdigit(c))x = x * 10 + c - '0',c = getchar();
if(flg)x = -x;
}
template <typename T,typename ...Y>
inline void read(T &x,Y&... X){read(x);read(X...);}
inline int readi(){static int res;read(res);return res;}
inline long long readll(){static long long res;read(res);return res;}
inline void flush(){
fwrite(obuf,sizeof(char),onow - obuf,stdout);
fflush(stdout);
onow = obuf;
}
inline void putchar(char c){
#ifndef ONLINE_JUDGE
::putchar(c);
#else
*onow++ = c;
if(onow == oed){
fwrite(obuf,sizeof(char),obufsiz,stdout);
onow = obuf;
}
#endif
}
template <typename T>
inline void write(T x,char split = ' '){
static unsigned char buf[64];
if(x < 0)putchar('-'),x = -x;
int p = 0;
do{
buf[++p] = x % 10;
x /= 10;
}while(x);
for(int i = p;i >= 1;i--)putchar(buf[i] + '0');
if(split != ' ')putchar(split);
}
inline void lf(){putchar('
');}
~IO(){
fwrite(obuf,sizeof(char),onow - obuf,stdout);
}
}io;
struct edge{int u,v;ll d;};
vector<edge> edges;
int n,m;
ll v[maxn];
namespace mset{
int f[maxn];
inline void init(){for(int i = 1;i <= n;i++)f[i] = i;}
inline int find(const int x){return x == f[x] ? x : f[x] = find(f[x]);}
inline void merge(const int a,const int b){
const int x = find(a),y = find(b);
f[x] = y;
}
}
ll ans;
inline void kruskal(){
mset::init();
sort(edges.begin(),edges.end(),[](const edge &a,const edge &b){
return a.d < b.d;
});
for(auto e : edges){
if(mset::find(e.u) == mset::find(e.v))continue;
ans += e.d;
mset::merge(e.u,e.v);
}
}
int p = 1;
int main(){
io.read(n,m);
for(int i = 1;i <= n;i++)io.read(v[i]);
for(int i = 1;i <= n;i++)
if(v[i] < v[p])p = i;
for(int i = 1;i <= n;i++)
if(i != p)edges.push_back(edge{i,p,v[i] + v[p]});
for(int u,v,i = 1;i <= m;i++){
io.read(u,v);const ll d = io.readll();
edges.push_back(edge{u,v,d});
}
kruskal();
io.write(ans,'
');
return 0;
}