1. 程式人生 > 實用技巧 >題解 CF1095F 【Make It Connected】

題解 CF1095F 【Make It Connected】

題目連結

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 = '\0';
					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 = '\0'){
			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 != '\0')putchar(split);
		}
		inline void lf(){putchar('\n');}
		~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,'\n');
	return 0;
}