南京站回来之后做的第一场cf,水一波博客
题意
有
n
n
n个城市,每个城市拥有电力,当且仅当作为供电站或者与任何一个拥有电力的城市之间架有电线.对于城市i来说,成为供电站的花费为
C
i
C_i
Ci,它与城市j之间架设电线的费用为
(
k
i
+
k
j
)
∗
(
∣
x
i
−
x
j
∣
+
∣
y
i
−
y
j
∣
)
(k_i + k_j) * (|x_i - x_j| + |y_i - y_j|)
(ki+kj)∗(∣xi−xj∣+∣yi−yj∣),你的任务是让这
n
n
n个城市都拥有电力,输出最小的花费,以及作为供电站的城市数量以及列表和架设的电线数量以及列表.
分析与解答
对于一个城市拥有电力,那么他一定只与一个(两个的花费大于一个的花费)供电站直接或者间接的连接,那么对于所有由
i
i
i提供电力的城市,构成了一个树(只需联通,多建边花费增加,因此不需要环). 我们考虑用一个超级点去连接所有的城市,边的长度为
C
i
C_i
Ci,同时连接所有的城市,边的长度为对应城市之间架设电线的花费.那么让所有的城市都拥有电力就相当于在包含超级点的图中找一棵最小生成树.取超级点与城市的连线,相当于将这个城市作为供电站,取城市直接的连线相当于架设电线.
代码
#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#define MAXN 4003
#define MAXM 3000000
using namespace std
;
int fa
[MAXN
];
struct node
{
long long x
,y
;
long long cost
,w
;
}nodes
[MAXN
];
int fa_find(int a
){ return fa
[a
] == a
? a
: fa
[a
] = fa_find(fa
[a
]); }
void unite(int a
,int b
)
{
a
= fa_find(a
);
b
= fa_find(b
);
fa
[a
] = b
;
}
struct edge
{
int f
,t
;
long long w
;
void var(int _f
,int _t
,long long _w
)
{
f
= _f
;
t
= _t
;
w
= _w
;
}
}edges
[MAXM
];
int cnt
;
int used
[MAXN
];
void kruskal()
{
long long res
= 0;
int tol
= 0;
int num
= 0;
for(int i
= 1;i
<= cnt
;i
++)
{
if(fa_find(edges
[i
].f
) != fa_find(edges
[i
].t
))
{
res
+= edges
[i
].w
;
used
[++tol
] = i
;
if(edges
[i
].f
== 0)
num
++;
unite(edges
[i
].f
,edges
[i
].t
);
}
}
printf("%lld\n",res
);
printf("%d\n",num
);
for(int i
= 1;i
<= tol
;i
++)
{
int now
= used
[i
];
if(edges
[now
].f
== 0)
printf("%d ",edges
[now
].t
);
}
printf("\n%d\n",tol
- num
);
for(int i
= 1;i
<= tol
;i
++)
{
int now
= used
[i
];
if(edges
[now
].f
!= 0)
printf("%d %d\n",edges
[now
].f
,edges
[now
].t
);
}
}
void init(int n
)
{
for(int i
= 0;i
<= n
;i
++)
fa
[i
] = i
;
}
long long cal(int i
,int j
)
{
return 1ll * (nodes
[i
].w
+ nodes
[j
].w
) * (abs(nodes
[i
].x
- nodes
[j
].x
) + abs(nodes
[i
].y
- nodes
[j
].y
));
}
bool
cmp(edge a
,edge b
)
{
return a
.w
< b
.w
;
}
int main()
{
int n
;
scanf("%d",&n
);
init(n
);
for(int i
= 1;i
<= n
;i
++)
scanf("%lld%lld",&nodes
[i
].x
,&nodes
[i
].y
);
for(int i
= 1;i
<= n
;i
++)
scanf("%lld",&nodes
[i
].cost
);
for(int i
= 1;i
<= n
;i
++)
scanf("%lld",&nodes
[i
].w
);
cnt
= 0;
for(int i
= 1;i
<= n
;i
++)
{
edges
[++cnt
].var(0,i
,nodes
[i
].cost
);
}
for(int i
= 1;i
<= n
;i
++)
for(int j
= i
+ 1;j
<= n
;j
++)
{
edges
[++cnt
].var(i
,j
,cal(i
,j
));
}
sort(edges
+ 1,edges
+ cnt
+ 1,cmp
);
kruskal();
return 0;
}