题目:https://codeforces.com/contest/1245/problem/D
题意:
有n座城市(用二维坐标表示,坐标大小小于1e6)
城市要供电,因此需要在城市里面修发电站,不同城市里面修发电站的代价不一样,分别是:
c[1],c[2],……,c[n]
城市和城市之间也可以修电线,给你个数组: k[1],k[2],……,k[n], 然后i,j城市之间修电线的代价为 城市间曼哈顿距离*(k[i]+k[j])
城市有电当且仅当城市里面有发电站或者能通过电线直接或者间接的和有发电站的城市相连
问最小的代价是多少,需要在哪些城市里面修发电站,哪些城市里面修电线
最小生成树的题目。
首先两两城市之间建边,权值为他们修路的代价
然后要考虑城市里面修发电站的情况,因此使用超级源点s,s向每座城市建边,权值为c[i]
关于记录发电站或者修电线的话,程序里面有注释
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> site;
const int maxn = 2005;
site num[maxn];
int c[maxn], k[maxn];
int Head[maxn*maxn], Nxt[maxn*maxn * 2], To[maxn*maxn * 2];
ll Val[maxn*maxn * 2];
int tot;
void add_edge(int fro, int to, ll val) {
Nxt[++tot] = Head[fro];
To[tot] = to;
Val[tot] = val;
Head[fro] = tot;
}
typedef pair<ll, site> P;
//这里的site的意思是 : site.first 为当前的点 site.second 为从那个点来的
priority_queue<P, vector<P>, greater<P> >Q;
ll dis[maxn];
int vis[maxn];
vector<int> ans_poi; //发电站
vector<site> ans_edge; //城市间修电线
ll Prim(int s,int ed) {
ll ans = 0;
int flag = 0;
memset(dis, INF, sizeof(dis));
dis[s] = 1;
Q.push(P(0, site(s, -1)));
P pp;
while (!Q.empty()) {
pp = Q.top();
int now = Q.top().second.first;
ll d = Q.top().first;
Q.pop();
if (vis[now])continue;
vis[now] = 1;
ans += d;
if (pp.second.second != -1) {
if (pp.second.first == 0) //有任何一个点是0的话说明是修发电站的
ans_poi.push_back(pp.second.second);
else if (pp.second.second == 0)
ans_poi.push_back(pp.second.first);
else //不然就是修电线的
ans_edge.push_back(site(pp.second.first, pp.second.second));
}
if (++flag == ed) return ans;
for (int i = Head[now]; i; i = Nxt[i]) {
int to = To[i];
ll val = Val[i];
if (dis[to] > val) {
dis[to] = val;
Q.push(P(dis[to], site(to, now)));
}
}
}
return ans;
}
int main() {
int n; cin >> n;
for (int i = 1; i <= n; i++)
scanf("%d %d", &num[i].first, &num[i].second);
for (int i = 1; i <= n; i++)
scanf("%d", c + i);
for (int i = 1; i <= n; i++)
scanf("%d", k + i);
ll sto;
for (int i = 1; i <= n; i++) { //城市之间建边
for (int j = 1; j < i; j++) {
sto = (ll)abs(num[i].first - num[j].first) + abs(num[i].second - num[j].second);
sto *= (k[i] + k[j]);
add_edge(i, j, sto);
add_edge(j, i, sto);
}
}
for (int i = 1; i <= n; i++) { //建发电站
add_edge(0, i, (ll)c[i]);
add_edge(i, 0, (ll)c[i]);
}
cout << Prim(0, n + 1) << endl; //最小生成树
cout << ans_poi.size() << endl;
for (int i = 0; i < ans_poi.size(); i++)
cout << ans_poi[i] << " ";
cout << endl;
cout << ans_edge.size() << endl;
for (int i = 0; i < ans_edge.size(); i++)
cout << ans_edge[i].first << " " << ans_edge[i].second << endl;
return 0;
}