【问题描述】
给出一张 n 个点 m 条边的无向图,每条边(ai,bi)有一个权值 wi 和费用 ci,表示这条边 每降低 1 的权值需要 ci 的花费。现在一共有 S 费用可以用来降低某些边的权值(可以降到 负数),求图中的一棵权值和最小的生成树并输出方案。
【输入描述】
第一行两个整数 n,m。 第二行 m 个整数 wi,表示每条边的权值。 第三行 m 个整数 ci,表示这条边每降低 1 的权值需要 ci 的花费 接下来 m 行,每行两个整数 ai,bi,表示 ai 到 bi 有边 最后一行一个整数 S
【输出描述】
求在代价不超过 S 的情况下,最终图中最小生成树权值和最小是多少。 输出最终在最小生成树中的边的编号,以及每条边的权值。
分析
首先我们最后一定是把所有的花费用在一条边上面。
假设我们已经求出了最小生成树, ci 最小值为 minc,那么花费一定都是在这条边上。
现在我们枚举不在树上的边,会和原来那棵树形成一个环,由于这条边本来不在最小生成树上,所以如果这条边最终在最小生成树上,一定是把所有花费都用在这条边上了,然后取代了环上最大的边。然后就倍增求环上最大边的编号和值就可以了,每次如果替换后答案更小,就记录换上的边和环上最大边(要取代的边)就可以了。
代码如下
#include <bits/stdc++.h>
#define N 200005
#define inf 2147483647
#define LL long long
using namespace std;
struct node{
int a, b, c, w, i, n;
bool operator < (const node & A) const{
if(w != A.w) return w < A.w;
return c < A.c;
}
}d[N * 2], e[N];
int h[N], f[N], vis[N], dep[N], val[N][20], st[N][20], fa[N], C[N], W[N], cnt, minc = inf;
LL ans, sum, ans1, z = 1;
int p[2], edg[N][20];
void cr(int a, int b, int c, int i){
d[++cnt].a = a; d[cnt].b = b; d[cnt].c = c; d[cnt].i = i; d[cnt].n = h[a]; h[a] = cnt;
}
int find(int x){
return x == f[x]? x: f[x] = find(f[x]);
}
void dfs(int a){
int i, b, c;
for(i = h[a]; i; i = d[i].n){
b = d[i].b; c = d[i].c;
if(b == fa[a]) continue;
fa[b] = a;
st[b][0] = a;
edg[b][0] = d[i].i;
val[b][0] = c;
dep[b] = dep[a] + 1;
dfs(b);
}
}
node getv(int a, int b){
int i, c, maxn = 0, id;
node t;
if(dep[a] < dep[b]) swap(a, b);
c = dep[a] - dep[b];
for(i = 19; i >= 0; i--){
if((1 << i & c)){
if(maxn < val[a][i]){
maxn = val[a][i];
id = edg[a][i];
}
a = st[a][i];
}
}
for(i = 19; i >= 0; i--){
if(st[a][i] != st[b][i]){
if(maxn < val[a][i]){
maxn = val[a][i];
id = edg[a][i];
}
if(maxn < val[b][i]){
maxn = val[b][i];
id = edg[b][i];
}
a = st[a][i];
b = st[b][i];
}
}
if(maxn < val[a][0]){
maxn = val[a][0];
id = edg[a][0];
}
if(maxn < val[b][0]){
maxn = val[b][0];
id = edg[b][0];
}
t.w = maxn; t.i = id;
return t;
}
int main(){
int i, j, n, m, a, b, c, w, s, hhh;
node t;
scanf("%d%d", &n, &m);
for(i = 1; i <= m; i++) scanf("%d", &W[i]);
for(i = 1; i <= m; i++) scanf("%d", &C[i]);
for(i = 1; i <= m; i++){
scanf("%d%d", &e[i].a, &e[i].b);
e[i].c = C[i], e[i].w = W[i]; e[i].i = i;
}
sort(e + 1, e + m + 1);
scanf("%d", &s);
for(i = 1; i <= n; i++) f[i] = i;
for(i = 1; i <= m; i++){
a = e[i].a, b = e[i].b, c = e[i].c, w = e[i].w;
if(find(a) != find(b)){
if(minc > c){
minc = c;
hhh = e[i].i;
}
sum += w;
f[find(b)] = find(a);
vis[i] = 1;
cr(a, b, w, e[i].i);
cr(b, a, w, e[i].i);
}
}
ans = sum - s / minc;
dfs(1);
for(i = 1; i <= 19; i++){
for(j = 1; j <= n; j++){
st[j][i] = st[st[j][i - 1]][i - 1];
if(val[j][i - 1] >= val[st[j][i - 1]][i - 1]){
val[j][i] = val[j][i - 1];
edg[j][i] = edg[j][i - 1];
}
else{
val[j][i] = val[st[j][i - 1]][i - 1];
edg[j][i] = edg[st[j][i - 1]][i - 1];
}
}
}
for(i = 1; i <= m; i++){
if(vis[i]) continue;
a = e[i].a, b = e[i].b, c = e[i].c, w = e[i].w;
if(c >= minc) continue;
t = getv(a, b);
ans1 = sum - (LL)t.w + (LL)w - (LL)s / c;
if(ans > ans1){
p[0] = t.i;
hhh = e[i].i;
ans = ans1;
}
}
printf("%lld\n", ans);
for(i = 1; i <= m; i++){
if(e[i].i == p[0]) continue;
if(e[i].i == hhh) printf("%d %d\n", e[i].i, e[i].w - s / e[i].c);
else if(vis[i]) printf("%d %d\n", e[i].i, e[i].w);
}
return 0;
}