您的位置:首页 > 其它

BZOJ 2001 City城市建设 (CDQ分治 + 并查集)

2018-03-03 20:41 225 查看
/**
思路: 参照 http://blog.sina.com.cn/s/blog_6e63f59e0101blum.html 主要想法就是solve(l, r)的时候, 把区间[l, r]修改的边先置为正无穷,找出一定不要的边,之后恢复状态置为负无穷,找出一定要的边,然后递归解决
solve(l, mid), solve(mid + 1, r)
*/
#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 2e4 + 10;
const int maxm = 5e4 + 10;
const int INF = 1e9 + 10;
using namespace std;

struct edge {
int x, y, w, pos;
bool operator < (edge e) const { return w < e.w; }
} e[20][maxm], d[maxm], t[maxm];
ll ans[maxm];
int n, m, q, x, y;
int cost[maxm], f[maxn], mp[maxm];
int id[maxm], co[maxm];

int findset(int x) { return x == f[x] ? x : f[x] = findset(f[x]); }
void init(int tot) { for(int i = 1; i <= tot; i++) f[d[i].x] = d[i].x, f[d[i].y] = d[i].y; }

void delete_edge(int &tot) {
int tmp = 0; init(tot); sort(d + 1, d + tot + 1);
for(int i = 1; i <= tot; i++) {
int nx = findset(d[i].x), ny = findset(d[i].y);
if(nx != ny) {  ///待选择边
f[nx] = ny; t[++tmp] = d[i]; mp[d[i].pos] = tmp;
} else if(d[i].w == INF) {
t[++tmp] = d[i]; mp[d[i].pos] = tmp;
}
}
for(int i = 1; i <= tmp; i++) d[i] = t[i]; tot = tmp;
}

void add_edge(int &tot, ll &cnt) {
int tmp = 0; init(tot); sort(d + 1, d + tot + 1);
for(int i = 1; i <= tot; i++) {
int nx = findset(d[i].x), ny = findset(d[i].y);
if(nx != ny) { f[nx] = ny; t[++tmp] = d[i]; }
}
for(int i = 1; i <= tmp; i++) { f[t[i].x] = t[i].x; f[t[i].y] = t[i].y; }
for(int i = 1; i <= tmp; i++) {
int nx = findset(t[i].x), ny = findset(t[i].y);
if(nx != ny && t[i].w != -INF) { f[nx] = ny; cnt += t[i].w; } ///必选边
}
tmp = 0;
for(int i = 1; i <= tot; i++) {
int nx = findset(d[i].x), ny = findset(d[i].y);
if(nx == ny) continue;
t[++tmp] = d[i]; t[tmp].x = nx; t[tmp].y = ny; mp[d[i].pos] = tmp; ///缩点,加入待选择边,映射关系改变
}
for(int i = 1; i <= tmp; i++) d[i] = t[i]; tot = tmp;
}

void solve(int l, int r, int tot, ll cnt, int lv) {
if(l == r) cost[id[l]] = co[l];
for(int i = 1; i <= tot; i++) e[lv][i].w = cost[e[lv][i].pos];
for(int i = 1; i <= tot; i++) d[i] = e[lv][i], mp[d[i].pos] = i; ///共有tot条待选择边,修改实际第pos条边就是修改d中第i条边
if(l == r) {
ans[l] = cnt; init(tot); sort(d + 1, d + tot + 1);
for(int i = 1; i <= tot; i++) {
int nx = findset(d[i].x), ny = findset(d[i].y);
if(nx != ny) { f[nx] = ny; ans[l] += d[i].w; }
}
return ;
}
///因为区间[L, R]是待处理区间(未确定的边), 故修改的情况一定有mp关系对应, 不用去初始化mp数组
for(int i = l; i <= r; i++) d[mp[id[i]]].w = -INF; add_edge(tot, cnt);  ///处理必选边
for(int i = l; i <= r; i++) d[mp[id[i]]].w = INF;  delete_edge(tot); ///处理必删边
for(int i = 1; i <= tot; i++) e[lv + 1][i] = d[i];  ///下一层分治的待选择边
int mid = (l + r) >> 1;
solve(l, mid, tot, cnt, lv + 1);
solve(mid + 1, r, tot, cnt, lv + 1);
}

int main() {
while(scanf("%d
4000
%d %d", &n, &m, &q) != EOF) {
for(int i = 1; i <= m; i++) {
scanf("%d %d %d", &x, &y, &cost[i]);
e[0][i].x = x; e[0][i].y = y; e[0][i].w = cost[i]; e[0][i].pos = i;
}
for(int i = 1; i <= q; i++) scanf("%d %d", &id[i], &co[i]);
solve(1, q, m, 0, 0);
for(int i = 1; i <= q; i++) printf("%lld\n", ans[i]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: