[CSP-S 2025] 道路修复题解
P14362 [CSP-S 2025] 道路修复 / road(民间数据)
题目背景
由于评测机性能差异,本题时限提升 1 秒。
测试数据未经过强度检验,可能存在错误做法获得较高分数,仅供参考。
- 2025.11.03 11:40:更新了一部分测试数据,可以卡掉部分时间复杂度 ∼O(m2k)\sim O(m2^k)∼O(m2k) 的做法。
- 11:56:修复上述数据存在不连通和重边的问题。
题目描述
C 国的交通系统由 nnn 座城市与 mmm 条连接两座城市的双向道路构成,第 iii (1≤i≤m1 \leq i \leq m1≤i≤m) 条道路连接城市 uiu_iui 和 viv_ivi。任意两座城市都能通过若干条道路相互到达。
然而,近期由于一场大地震,所有 mmm 条道路都被破坏了,修复第 iii (1≤i≤m1 \leq i \leq m1≤i≤m) 条道路的费用为 wiw_iwi。与此同时,C 国还有 kkk 个准备进行城市化改造的乡镇。对于第 jjj (1≤j≤k1 \leq j \leq k1≤j≤k) 个乡镇,C 国对其进行城市化改造的费用为 cjc_jcj。在城市化改造完第 jjj (1≤j≤k1 \leq j \leq k1≤j≤k) 个乡镇后,可以在这个乡镇与原来的 nnn 座城市间建造若干条道路,其中在它与第 iii (1≤i≤n1 \leq i \leq n1≤i≤n) 座城市间建造一条道路的费用为 aj,ia_{j,i}aj,i。C 国可以在这 kkk 个乡镇中选择任意多个进行城市化改造,也可以不选择任何乡镇进行城市化改造。
为尽快恢复城市间的交通,C 国政$ $府希望以最低的费用将原有的 nnn 座城市两两连通,也即任意两座原有的城市都能通过若干条修复或新建造的道路相互到达。你需要帮助他们求出,将原有的 nnn 座城市两两连通的最小费用。
输入格式
输入的第一行包含三个非负整数 n,m,kn, m, kn,m,k,分别表示原有的城市数量、道路数量和准备进行城市化改造的乡镇数量。
输入的第 i+1i+1i+1 (1≤i≤m1 \leq i \leq m1≤i≤m) 行包含三个非负整数 ui,vi,wiu_i, v_i, w_iui,vi,wi,表示第 iii 条道路连接的两座城市与修复该道路的费用。
输入的第 j+m+1j+m+1j+m+1 (1≤j≤k1 \leq j \leq k1≤j≤k) 行包含 n+1n+1n+1 个非负整数 cj,aj,1,aj,2,…,aj,nc_j, a_{j,1}, a_{j,2}, \ldots, a_{j,n}cj,aj,1,aj,2,…,aj,n,分别表示将第 jjj 个乡镇进行城市化改造的费用与在该乡镇与原有的城市间建造道路的费用。
输出格式
输出一行一个非负整数,表示将原有的 nnn 座城市两两连通的最小费用。
输入输出样例 #1
输入 #1
4 4 2
1 4 6
2 3 7
4 2 5
4 3 4
1 1 8 2 4
100 1 3 2 4
输出 #1
13
说明/提示
【样例 1 解释】
C 国政$ $府可以选择修复第 333 条和第 444 条道路,然后将第 111 个乡镇进行城市化改造,并建造它与第 1,31,31,3 座城市间的道路,总费用为 5+4+1+1+2=135 + 4 + 1 + 1 + 2 = 135+4+1+1+2=13。可以证明,不存在比 131313 更小的费用能使原有的 444 座城市两两连通。
【样例 2】
见选手目录下的 road/road2.inroad/road2.inroad/road2.in 与 road/road2.ansroad/road2.ansroad/road2.ans。
该样例满足测试点 11,1211,1211,12 的约束条件。
【样例 3】
见选手目录下的 road/road3.inroad/road3.inroad/road3.in 与 road/road3.ansroad/road3.ansroad/road3.ans。
该样例满足测试点 13,1413,1413,14 的约束条件。
【样例 4】
见选手目录下的 road/road4.inroad/road4.inroad/road4.in 与 road/road4.ansroad/road4.ansroad/road4.ans。
该样例满足测试点 15,1615,1615,16 的约束条件。
【数据范围】
对于所有测试数据,保证:
- 1≤n≤1041 \leq n \leq 10^41≤n≤104,1≤m≤1061 \leq m \leq 10^61≤m≤106,0≤k≤100 \leq k \leq 100≤k≤10;
- 对于所有 1≤i≤m1 \leq i \leq m1≤i≤m,均有 1≤ui,vi≤n1 \leq u_i, v_i \leq n1≤ui,vi≤n, ui≠viu_i \neq v_iui=vi 且 0≤wi≤1090 \leq w_i \leq 10^90≤wi≤109;
- 对于所有 1≤j≤k1 \leq j \leq k1≤j≤k,均有 0≤cj≤1090 \leq c_j \leq 10^90≤cj≤109;
- 对于所有 1≤j≤k1 \leq j \leq k1≤j≤k,1≤i≤n1 \leq i \leq n1≤i≤n, 均有 0≤aj,i≤1090 \leq a_{j,i} \leq 10^90≤aj,i≤109;
- 任意两座原有的城市都能通过若干条原有的道路相互到达。
::cute-table{tuack}
| 测试点编号 | n≤n \leqn≤ | m≤m \leqm≤ | k≤k \leqk≤ | 特殊性质 |
|---|---|---|---|---|
| 1∼41 \sim 41∼4 | 10410^4104 | 10610^6106 | 000 | 无 |
| 5,65, 65,6 | 10310^3103 | 10510^5105 | 555 | A |
| 7,87, 87,8 | ^ | ^ | ^ | 无 |
| 9,109, 109,10 | ^ | 10610^6106 | ^ | A |
| 11,1211, 1211,12 | ^ | ^ | ^ | 无 |
| 13,1413, 1413,14 | ^ | ^ | 101010 | A |
| 15,1615, 1615,16 | ^ | ^ | ^ | 无 |
| 17,1817, 1817,18 | 10410^4104 | ^ | 555 | A |
| 19,2019, 2019,20 | ^ | ^ | ^ | 无 |
| 21∼2521 \sim 2521∼25 | ^ | ^ | 101010 | ^ |
特殊性质 A:对于所有 1≤j≤k1 \leq j \leq k1≤j≤k,均有 cj=0c_j = 0cj=0 且均存在 1≤i≤n1 \leq i \leq n1≤i≤n 满足 aj,i=0a_{j,i} = 0aj,i=0。
思路
首先容易想到直接暴力枚举加点,可以发现,即使加点,可最小生成树外边必不选,所以,直接暴力。
暴力代码
#include<bits/stdc++.h>
using namespace std;
long long n,m,k,c[13],a[13][10024],bk=0,ed=0,f[10024],xx,yy,op=0,m2=0,m3=0;
struct one{
int u,v;
int w;
}b[2000006],b2[1000005],b3[2000005];
bool cmp(one a1,one b1){
return a1.w<b1.w;
}
inline int find(int a1){
if(a1==f[a1]){
return a1;
}
else{
f[a1]=find(f[a1]);
return f[a1];
}
}
bool bo[100005];
inline void abc(int a1){
if(a1==k+1){
long long op2=0,uz=0,us=0;
m3=m2;
for(int i=1;i<=m2;i++){
b3[i]=b2[i];
}
for(int i=1;i<=k;i++){
if(bo[i]==1){
us++;
op2+=c[i];
for(int j=1;j<=n;j++){
++m3;
b3[m3].u=i+n;
b3[m3].v=j;
b3[m3].w=a[i][j];
}
}
}
sort(b3+1,b3+m3+1,cmp);
for(int i=1;i<=n+k;i++){
f[i]=i;
}
for(int i=1;i<=m3;i++){
xx=find(b3[i].u);
yy=find(b3[i].v);
if(xx!=yy){
f[yy]=xx;
uz++;
op2+=b3[i].w;
}
if(op2>op||uz==n+us-1){
break;
}
}
//cout<<op2<<endl;
op=min(op,op2);
return ;
}
else{
bo[a1]=1;
abc(a1+1);
bo[a1]=0;
abc(a1+1);
return ;
}
}
int main(){
//freopen("road4.in","r",stdin);
cin>>n>>m>>k;
for(int i=1;i<=m;i++){
scanf("%ld%ld%lld",&b[i].u,&b[i].v,&b[i].w);
}
bk=0;
for(int i=1;i<=k;i++){
cin>>c[i];
if(c[i]!=0){
bk=1;
}
ed=0;
for(int j=1;j<=n;j++){
scanf("%lld",&a[i][j]);
if(a[i][j]==0){
ed=1;
}
}
if(ed==0){
bk=1;
}
}
if(bk!=1){
for(int i=1;i<=k;i++){
for(int j=1;j<=n;j++){
++m;
b[m].u=i+n;
b[m].v=j;
b[m].w=a[i][j];
}
}
sort(b+1,b+m+1,cmp);
for(int i=1;i<=n+k;i++){
f[i]=i;
}
for(int i=1;i<=m;i++){
xx=find(b[i].u);
yy=find(b[i].v);
if(xx!=yy){
f[yy]=xx;
op+=b[i].w;
}
}
cout<<op<<endl;
}
else{
sort(b+1,b+m+1,cmp);
for(int i=1;i<=n;i++){
f[i]=i;
}
for(int i=1;i<=m;i++){
xx=find(b[i].u);
yy=find(b[i].v);
if(xx!=yy){
f[yy]=xx;
b2[++m2]=b[i];
op+=b[i].w;
}
}
abc(1);
cout<<op<<endl;
}
return 0;
}
发现过不了,考虑优化排序,直接预处理离散化然后桶排序即可。
代码见下
#include<bits/stdc++.h>
using namespace std;
int n,m,k,c[13],a[13][10024],a2[13][10024],bk=0,ed=0,f[10024],xx,yy,js[200005],qd=0;
long long op=0,m2=0,m3=0;
struct one{
int u,v;
int w,w2;
}b[2000006],b2[1000005],b3[2000005];
bool cmp(one a1,one b1){
return a1.w<b1.w;
}
inline int find(int a1){
if(a1==f[a1]){
return a1;
}
else{
f[a1]=find(f[a1]);
return f[a1];
}
}
bool bo[100005];
vector<one> v[200005];
inline void abc(int a1){
if(a1==k+1){
long long op2=0,uz=0,us=0;
m3=0;
for(int i=1;i<=qd;i++){
v[i].clear();
}
for(int i=1;i<=m2;i++){
v[b2[i].w2].push_back(b2[i]);
}
for(int i=1;i<=k;i++){
if(bo[i]==1){
us++;
op2+=c[i];
for(int j=1;j<=n;j++){
v[a2[i][j]].push_back({i+n,j,a[i][j],a2[i][j]});
}
}
}
for(int i=1;i<=qd;i++){
for(int j=0;j<v[i].size();j++){
b3[++m3]=v[i][j];
}
}
for(int i=1;i<=n+k;i++){
f[i]=i;
}
for(int i=1;i<=m3;i++){
xx=find(b3[i].u);
yy=find(b3[i].v);
if(xx!=yy){
f[yy]=xx;
uz++;
op2+=b3[i].w;
}
if(op2>op||uz==n+us-1){
break;
}
}
//cout<<op2<<endl;
op=min(op,op2);
return ;
}
else{
bo[a1]=1;
abc(a1+1);
bo[a1]=0;
abc(a1+1);
return ;
}
}
map<int,int> mp;
int main(){
//freopen("road4.in","r",stdin);
cin>>n>>m>>k;
for(int i=1;i<=m;i++){
scanf("%ld%ld%lld",&b[i].u,&b[i].v,&b[i].w);
}
bk=0;
for(int i=1;i<=k;i++){
cin>>c[i];
if(c[i]!=0){
bk=1;
}
ed=0;
for(int j=1;j<=n;j++){
scanf("%lld",&a[i][j]);
js[++js[0]]=a[i][j];
if(a[i][j]==0){
ed=1;
}
}
if(ed==0){
bk=1;
}
}
if(bk!=1){
for(int i=1;i<=k;i++){
for(int j=1;j<=n;j++){
++m;
b[m].u=i+n;
b[m].v=j;
b[m].w=a[i][j];
}
}
sort(b+1,b+m+1,cmp);
for(int i=1;i<=n+k;i++){
f[i]=i;
}
for(int i=1;i<=m;i++){
xx=find(b[i].u);
yy=find(b[i].v);
if(xx!=yy){
f[yy]=xx;
op+=b[i].w;
}
}
cout<<op<<endl;
}
else{
sort(b+1,b+m+1,cmp);
for(int i=1;i<=n;i++){
f[i]=i;
}
for(int i=1;i<=m;i++){
xx=find(b[i].u);
yy=find(b[i].v);
if(xx!=yy){
f[yy]=xx;
b2[++m2]=b[i];
op+=b[i].w;
js[++js[0]]=b[i].w;
}
}
sort(js+1,js+js[0]+1);
for(int i=1;i<=js[0];i++){
if(i==1||js[i]!=js[i-1]){
mp[js[i]]=++qd;
}
}
for(int i=1;i<=k;i++){
for(int j=1;j<=n;j++){
a2[i][j]=mp[a[i][j]];
}
}
for(int i=1;i<=m2;i++){
b2[i].w2=mp[b2[i].w];
}
abc(1);
cout<<op<<endl;
}
return 0;
}

浙公网安备 33010602011771号