「BZOJ 2001」城市建设
给出一个图,要求每次修改两点之间的边权,并求出修改后的最小生成树的边权之和。 \((n \le 20000, \ m,q \le 50000)\)
分析
CDQ 分治 + MST
参考顾昱洲神犇的 PPT
我们对询问进行分治,对于当前区间 [l, r] 中的边,我们有如下两种操作:
Contraction:
将当前分治区间内所有的边权设为 \(-\infty\) ,跑一遍 MST ,在 MST 中且权值不为 \(-\infty\) 的边是必须边,也就是无论如何都必须选的边。我们可以把这些边上的点合并起来,做到缩点。
Reduction:
将当前分治区间内所有的边权设为 \(\infty\) ,跑一边 MST ,不在 MST 中且权值不为 \(\infty\) 的边为无用边,也就是无论如何都不会被选到的边。我们可以把这些边去掉。
我们做一遍 C-R-C ,可以将数据规模由 (n, m) 缩小到 (k + 1, 2k) ,其中 k 是待修改的边。
但其实做一遍 C-R 就好了。
代码
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 50005
#define INF 0x3f3f3f3f
#define tie0 cin.tie(0),cout.tie(0)
#define fastio ios::sync_with_stdio(false)
#define File(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;
typedef long long ll;
template <typename T> inline void read(T &x) {
T f = 1; x = 0; char c;
for (c = getchar(); !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
x *= f;
}
struct edge {
int u, v, w, id;
friend bool operator < (edge x, edge y) {
return x.w < y.w;
}
} e[55][N], re[N], du[N];
struct query { int x, d; } q[N];
int n, m, Q;
ll ans[N];
int pos[N], cnt[N], val[N], fa[N];
void reset(int tot) {
for (int i = 1; i <= tot; ++i)
fa[re[i].u] = re[i].u, fa[re[i].v] = re[i].v;
}
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
void Contraction(int &tot, ll &res) {
int num = 0;
reset(tot);
sort(re + 1, re + 1 + tot);
for (int i = 1; i <= tot; ++i) {
int f1 = find(re[i].u), f2 = find(re[i].v);
if (f1 == f2) continue;
fa[f2] = f1, du[++num] = re[i];
}
for (int i = 1; i <= num; ++i) fa[du[i].u] = du[i].u, fa[du[i].v] = du[i].v;
for (int i = 1; i <= num; ++i) {
if (du[i].w == -INF) continue;
int f1 = find(du[i].u), f2 = find(du[i].v);
fa[f2] = f1, res += du[i].w;
}
num = 0;
for (int i = 1; i <= tot; ++i) {
int f1 = find(re[i].u), f2 = find(re[i].v);
if (f1 == f2) continue;
du[++num] = re[i], pos[re[i].id] = num;
du[num].u = f1, du[num].v = f2;
}
tot = num;
for (int i = 1; i <= tot; ++i) re[i] = du[i];
}
void Reduction(int &tot) {
int num = 0;
reset(tot);
sort(re + 1, re + 1 + tot);
for (int i = 1; i <= tot; ++i) {
int f1 = find(re[i].u), f2 = find(re[i].v);
if (f1 == f2) {
if (re[i].w == INF) du[++num] = re[i], pos[re[i].id] = num;
continue;
}
fa[f2] = f1;
du[++num] = re[i], pos[re[i].id] = num;
}
tot = num;
for (int i = 1; i <= tot; ++i) re[i] = du[i];
}
void CDQ(int l, int r, int k, ll res) {
if (l == r) val[q[l].x] = q[l].d;
int tot = cnt[k], mid = l + r >> 1;
for (int i = 1; i <= tot; ++i) {
e[k][i].w = val[e[k][i].id];
re[i] = e[k][i], pos[re[i].id] = i;
}
if (l == r) {
reset(tot);
sort(re + 1, re + 1 + tot);
for (int i = 1; i <= tot; ++i) {
int f1 = find(re[i].u), f2 = find(re[i].v);
if (f1 == f2) continue;
fa[f2] = f1, res += re[i].w;
}
ans[l] = res;
return;
}
for (int i = l; i <= r; ++i) re[pos[q[i].x]].w = -INF;
Contraction(tot, res);
for (int i = l; i <= r; ++i) re[pos[q[i].x]].w = INF;
Reduction(tot);
cnt[k+1] = tot;
for (int i = 1; i <= tot; ++i) e[k+1][i] = re[i];
CDQ(l, mid, k + 1, res), CDQ(mid + 1, r, k + 1, res);
}
int main() {
int u, v, w;
read(n), read(m), read(Q);
for (int i = 1; i <= m; ++i) {
read(u), read(v), read(w);
e[0][i] = (edge){u, v, val[i] = w, i};
}
for (int i = 1; i <= Q; ++i) read(q[i].x), read(q[i].d);
cnt[0] = m;
CDQ(1, Q, 0, 0);
for (int i = 1; i <= Q; ++i) printf("%lld\n", ans[i]);
return 0;
}

浙公网安备 33010602011771号