「BZOJ 2001」城市建设

给出一个图,要求每次修改两点之间的边权,并求出修改后的最小生成树的边权之和。 \((n \le 20000, \ m,q \le 50000)\)

Luogu

BZOJ

分析

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;
}
posted @ 2020-02-01 16:36  小蒟蒻hlw  阅读(130)  评论(0)    收藏  举报