Luogu P14362 [CSP-S 2025] 道路修复 / road 题解
考虑到对于 \(k\) 个乡镇明显可以状压枚举所有乡镇选择的方案,然后暴力计算目前选择的乡镇与 \(n\) 个城市的最小生成树,此时边数时 \(O(kn+m)\) 级别的,那么时间复杂度是 \(O(2^k(kn+m)log\ (kn+m))\) 的,这样必然会超时。我们于是考虑减少边的个数,我们不难发现,在最开始只有 \(n\) 个城市时,\(m\) 条边中不是最小生成树的边在加入了新的乡镇后必然也是对答案没有贡献的,所以我们可以不管这些无贡献的边,将每次求最小生成树的边数降到 \(O(kn)\) 级别的。但此时我们又发现如果每次求最小生成树时对所有边进行排序,时间复杂度比较劣,我们可以考虑预处理出每个城镇的 \(n\) 条边的序列使其有序,这样每次求最小生成树时便可以使用归并排序使排序复杂度更优,此时时间复杂度是 \(O(2^kkn \alpha(n))\) 的。
#include<bits/stdc++.h>
using namespace std;
int n,m,k,f[10015],c[15];
struct node{
int u,v,w;
}s[1000005],nw[10005],init[15][10005],t[2][120005];
long long ans=0;
int getf(int x){return (x==f[x])?x:f[x]=getf(f[x]);}
int main(){
cin>>n>>m>>k;
for(int i=0;i<m;i++)cin>>s[i].u>>s[i].v>>s[i].w;
sort(s,s+m,[](node x,node y){return x.w<y.w;});
int tmp=0,p=0;
for(int i=1;i<=n;i++)f[i]=i;
while(tmp<n-1){
int fx=getf(s[p].u),fy=getf(s[p].v);
if(fx!=fy){
f[fx]=fy;
ans+=s[p].w;
tmp++;
nw[tmp]=s[p];
}
p++;
}
for(int i=1;i<=k;i++){cin>>c[i];for(int j=1;j<=n;j++){cin>>init[i][j].w;init[i][j].u=n+i;init[i][j].v=j;}sort(init[i]+1,init[i]+1+n,[](node x,node y){return x.w<y.w;});}
for(int i=1;i<(1<<k);i++){
for(int j=1;j<=n+k;j++)f[j]=j;
int sum=0,id=0;
int num=n+__builtin_popcount(i),cnt=0,kk=1;
long long anss=0;
for(int j=1;j<n;j++)t[id][++sum]=nw[j];
for(int j=1;j<=k;j++){
if(!((1<<(j-1))&i))continue;
anss+=c[j];
id^=1;
int l=1,r=1,summ=0;
while(l<=sum&&r<=n){
if(t[id^1][l].w<=init[j][r].w)t[id][++summ]=t[id^1][l++];
else t[id][++summ]=init[j][r++];
}
while(l<=sum)t[id][++summ]=t[id^1][l++];
while(r<=n)t[id][++summ]=init[j][r++];
sum=summ;
}
while(cnt<num-1){
int fx=getf(t[id][kk].u),fy=getf(t[id][kk].v);
if(fx!=fy){
f[fx]=fy;
anss+=t[id][kk].w;
cnt++;
}
kk++;
}
ans=min(ans,anss);
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号