【题目描述】
PS国是一个拥有诸多城市的大国,国王Louis为城市的交通建设可谓绞尽脑汁。Louis可以在某些城市之间修建道路,在不同的城市之间修建道路需要不同的花费。Louis希望建造最少的道路使得国内所有的城市连通。但是由于某些因素,城市之间修建道路需要的花费会随着时间而改变,Louis会不断得到某道路的修建代价改变的消息,他希望每得到一条消息后能立即知道使城市连通的最小花费总和, Louis决定求助于你来完成这个任务。
【输入】
文件第一行包含三个整数N,M,Q,分别表示城市的数目,可以修建的道路个数,及收到的消息个数。 接下来M行,第i+1行有三个用空格隔开的整数Xi,Yi,Zi(1≤Xi,Yi≤n, 0≤Zi≤50000000),表示在城市Xi与城市Yi之间修建道路的代价为Zi。接下来Q行,每行包含两个数k,d,表示输入的第k个道路的修建代价修改为d(即将Zk修改为d)。
【输出】
输出包含Q行,第i行输出得知前i条消息后使城市连通的最小花费总和。
【输入样例】
5 5 3 1 2 1 2 3 2 3 4 3 4 5 4 5 1 5 1 6 1 1 5 3
【输出样例】
14 10 9
【提示】
【数据规模】 对于20%的数据, n≤1000,m≤6000,Q≤6000。 有20%的数据,n≤1000,m≤50000,Q≤8000,修改后的代价不会比之前的代价低。 对于100%的数据, n≤20000,m≤50000,Q≤50000。
做完本题可以说开启了cdq分治的全新版本。它提供了分治的另一个方向:分而不治。这道题的精华就在于它的分治并不是在处理问题,而是在缩小问题规模。具体的来说,就是在每一层分治下缩小图的规模。
考虑每一层分治进行两个操作:
1、删除一定不会选择的边:考虑把当前区间内所有要修改的边权值赋值为inf,此时做一遍最小生成树,可以知道这时候不在最小生成树内的非修改边一定是不会选择的,这些边以后也一定不会选择,因此可以删掉。
2、选定一些一定会选的边:考虑把当前区间内所有要修改的边权值赋值为-inf,此时做一遍最小生成树,可以知道这时候在最小生成树内的非-inf边是一定要选的,我们选择这些边,再用选定的边组成的联通快缩点传给下一层。
#include<cstdio> #include<stack> #include<cstring> #include<algorithm> #define LL long long #define For(i, a, b) for(register int i = a; i <= b; ++i) using namespace std; const int maxn = 1e5 + 5, inf = 0x3f3f3f3f; int nds[21], eds[21], w[maxn]; int n, m, q; struct edge { int u, v, w, ord, next, chs; }e[21][maxn]; struct Qs { int id, w; } Q[maxn]; edge GRP[maxn]; bool cmp_w(const edge &A, const edge &B) { return A.w < B.w; } namespace DSU { int fa[maxn]; void init(int n) {For(i, 1, n) fa[i] = i;} int find(int x) {return fa[x] == x ? x : fa[x] = find(fa[x]);} int combine(int x, int y) { x = find(x), y = find(y); if(x == y) return 0; return fa[x] = y; } } int cont[maxn], tp, vis[maxn], oid[maxn]; void Shrink(int now, int n, int & m) { int cnt = 0; DSU::init(n); sort(GRP + 1, GRP + m + 1, cmp_w); For(i, 1, m) { if(DSU::combine(GRP[i].u, GRP[i].v)) GRP[i].chs = 1, ++cnt; if(cnt == n - 1) break; } cnt = 0; For(i, 1, m) if(!(!GRP[i].chs && GRP[i].w != inf)) GRP[++cnt] = GRP[i], GRP[cnt].chs = 0, oid[GRP[cnt].ord] = cnt; m = cnt; } LL Lock(int now, int & n, int & m) { int cnt = 0, tmp, ru, rv; LL ans = 0; int tp = 0; DSU::init(n); sort(GRP + 1, GRP + m + 1, cmp_w); For(i, 1, m) { if(DSU::combine(GRP[i].u, GRP[i].v)) GRP[i].chs = 1, ++tp; if(tp == n - 1) break; } tp = 0, DSU::init(n); For(i, 1, m) if(GRP[i].chs && GRP[i].w != -inf) if(DSU::combine(GRP[i].u, GRP[i].v)) ans += GRP[i].w; For(i, 1, n) if(!vis[tmp = DSU::find(i)]) vis[tmp] = 1, cont[tmp] = ++tp; n = tp, tp = 0; For(i, 1, m) { ru = cont[DSU::find(GRP[i].u)]; rv = cont[DSU::find(GRP[i].v)]; oid[GRP[i].ord] = 0; vis[GRP[i].u] = vis[GRP[i].v] = 0; if(ru == rv) continue; GRP[++tp] = GRP[i], GRP[tp].chs = 0; GRP[tp].u = ru, GRP[tp].v = rv; oid[GRP[tp].ord] = tp; } return m = tp, ans; } LL ans[maxn]; void cdq(int l, int r, int now, LL tmp) { int n = nds[now], m = eds[now]; if(l == r) w[Q[l].id] = Q[l].w; For(i, 1, m) e[now][i].w = w[e[now][i].ord]; For(i, 1, m) GRP[i] = e[now][i], oid[GRP[i].ord] = i; if(l == r) { sort(GRP + 1, GRP + m + 1, cmp_w), DSU::init(n); For(i, 1, m) if(DSU::combine(GRP[i].u, GRP[i].v)) ans[l] += GRP[i].w; ans[l] += tmp; return ; } For(i, l, r) GRP[oid[Q[i].id]].w = inf; Shrink(now, n, m); For(i, l, r) GRP[oid[Q[i].id]].w = -inf; tmp += Lock(now, n, m); For(i, 1, m) e[now + 1][i] = GRP[i]; nds[now + 1] = n, eds[now + 1] = m; int mid = l + r >> 1; cdq(l, mid, now + 1, tmp); cdq(mid + 1, r, now + 1, tmp); } int main() { scanf("%d%d%d", &n, &m, &q); For(i, 1, m) scanf("%d%d%d", &e[0][i].u, &e[0][i].v, &w[i]), e[0][i].ord = i; For(i, 1, q) scanf("%d%d", &Q[i].id, &Q[i].w); nds[0] = n, eds[0] = m, cdq(1, q, 0, 0); For(i, 1, q) printf("%lld\n", ans[i]); return 0; }
没有帐号? 立即注册