最小生成树的应用 [CSP-S 2025] 道路修复

题目

显而易见, 当 \(k=0\) 时, 我们要求的是最小生成树. kruskal 解决即可.
现在我们要思考的是当 \(k\in[1,10],\ k\in\mathbb{N^*}\) 时的情况.

显然我们可以枚举每一种翻新乡镇的情况, 时间复杂度为 \(O\left(2^k\right)\). 在此基础上跑最小生成树即可. 但是这样时间复杂度变为 \(O\left(2^k(m+nk)\log(m+nk)\right)\).
于是我们不妨先跑一遍 kruskal, 即先得到一个最优答案, 建树. 再在此基础上加边加点, 即将乡镇城市化, 继续跑 kruskal, 时间复杂度预计为 \(O\left((nk)\log (nk)+m\log m+2^k(n+k)\right)\).

初建树

inline void kruskal_inint(){
	for(int i=1;i<=n;P(i))f[i]=i;
	for(auto [u,v,w]:e){
		int u1=find(u),v1=find(v);
		if(u1==v1)continue;
		f[u1]=v1,new_e.push_back((NODE){u,v,w}),ans+=w;
		if((int)new_e.size()==n-1)break;
	}
}
signed main(){
	for(int i=1,u,v,w;i<=m;P(i)){
		std::cin>>u>>v>>w;
		e.push_back((NODE){u,v,w});
	}
	std::sort(e.begin(),e.end());
	kruskal_inint();
	if(!k){
		std::cout<<ans;
		exit(0);
	}
}

加边加点:

for(int i=1;i<=k;P(i)){
	std::cin>>cost[i];
	for(int j=1,c;j<=n;P(j)){
		std::cin>>c;
		new_e.push_back((NODE){j,n+i,c});
    }
}
std::sort(new_e.begin(),new_e.end());

枚举加边加点情况:

inline LL kruskal(int state){
	LL ans(0),country_cnt(0);
	int cnt(0);
	for(int i=1;i<=k;P(i)){// 先把 state 给转化成具体的加边加点情况
		if((state>>(i-1))&1)build[i]=true,ans+=cost[i],P(country_cnt);
		else build[i]=false;
	}
	for(int i=1;i<=n_all;P(i))f[i]=i;//并查集
	for(auto [u,v,w]:new_e){//kruskal
		if(v>n&&!build[v-n])continue;
		int v1=find(v),u1=find(u);
		if(v1==u1)continue;
		f[u1]=v1,ans+=w,P(cnt);
		if(cnt==n+country_cnt-1)return ans;
	}
	return 1e18;
}
signed main(){
    for(int i=1;i<country_cnt;P(i))
	ans=std::min(ans,kruskal(i));
}

全代码:

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<vector>
#define P(A) A=-~A
#define NUMBER1 1000000
#define NUMBER2 10
typedef long long LL;
struct NODE{
	int u,v;
	LL w;
	bool operator<(const NODE &A)const{return w<A.w;}
	NODE(int u=0,int v=0,LL w=0):u(u),v(v),w(w){}
};
std::vector<NODE>e,new_e;
int n,m,k,f[NUMBER1+NUMBER2+5],n_all;
LL cost[NUMBER2+5],ans(0);
bool build[NUMBER2+5];
inline int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
inline void kruskal_inint(){
	for(int i=1;i<=n;P(i))f[i]=i;
	for(auto [u,v,w]:e){
		int u1=find(u),v1=find(v);
		if(u1==v1)continue;
		f[u1]=v1,new_e.push_back((NODE){u,v,w}),ans+=w;
		if((int)new_e.size()==n-1)break;
	}
}
inline LL kruskal(int state){
	LL ans(0),country_cnt(0);
	int cnt(0);
	for(int i=1;i<=k;P(i)){
		if((state>>(i-1))&1)build[i]=true,ans+=cost[i],P(country_cnt);
		else build[i]=false;
	}
	for(int i=1;i<=n_all;P(i))f[i]=i;
	for(auto [u,v,w]:new_e){
		if(v>n&&!build[v-n])continue;
		int v1=find(v),u1=find(u);
		if(v1==u1)continue;
		f[u1]=v1,ans+=w,P(cnt);
		if(cnt==n+country_cnt-1)return ans;
	}
	return 1e18;
}
signed main(){
	std::cin.tie(nullptr)->std::ios::sync_with_stdio(false);
	std::cout.tie(nullptr);
	std::cin>>n>>m>>k;
	n_all=n+k;
	int country_cnt=1<<k;
	for(int i=1,u,v,w;i<=m;P(i)){
		std::cin>>u>>v>>w;
		e.push_back((NODE){u,v,w});
	}
	std::sort(e.begin(),e.end());
	kruskal_inint();
	if(!k){
		std::cout<<ans;
		exit(0);
	}
	for(int i=1;i<=k;P(i)){
		std::cin>>cost[i];
		for(int j=1,c;j<=n;P(j)){
			std::cin>>c;
			new_e.push_back((NODE){j,n+i,c});
		}
	}
	std::sort(new_e.begin(),new_e.end());
	for(int i=1;i<country_cnt;P(i))
		ans=std::min(ans,kruskal(i));
	std::cout<<ans;
	return 0;
}
posted @ 2025-12-07 19:30  SHOJYS  阅读(1)  评论(0)    收藏  举报