2001: [Hnoi2010]City 城市建设
Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 1098 Solved: 539
[ Submit][ Status][ Discuss]
Description
PS国是一个拥有诸多城市的大国,国王Louis为城市的交通建设可谓绞尽脑汁。Louis可以在某些城市之间修建道路,在不同的城市之间修建道路需要不同的花费。Louis希望建造最少的道路使得国内所有的城市连通。但是由于某些因素,城市之间修建道路需要的花费会随着时间而改变,Louis会不断得到某道路的修建代价改变的消息,他希望每得到一条消息后能立即知道使城市连通的最小花费总和, Louis决定求助于你来完成这个任务。
Input
文件第一行包含三个整数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)。
Output
输出包含Q行,第i行输出得知前i条消息后使城市连通的最小花费总和。
Sample Input
1 2 1
2 3 2
3 4 3
4 5 4
5 1 5
1 6
1 1
5 3
Sample Output
10
9
HINT
【数据规模】 对于20%的数据, n≤1000,m≤6000,Q≤6000。 有20%的数据,n≤1000,m≤50000,Q≤8000,修改后的代价不会比之前的代价低。 对于100%的数据, n≤20000,m≤50000,Q≤50000。
Source
CDQ分治,,啊。。想也想不到
传送门这里的ppt有很详细的介绍了
所以就补充一点笔记吧。。
每次修改只是改了一条边的权值,因此,修改之后的MST有着极大的相似性,借此分治
定义过程Solve(l,r)为处理(l,r)的询问,然后将图修改为做完(l,r)修改之后的状态
为了很好地实现修改和询问操作,要明确一些事情,,
一开始得到图G,一个点集V和一个边集E,要做MST
假如强制E的某个子集S中所有边不选,然后做一次MST,假设得到一棵树T
那么E - T中不属于S的所有边,无论S中的所有边边权如何改变,永远都不会被选
因为我们已经能找到更优的东西,且S的改变无法影响E - T中不属于S的边
再强制S中所有边优先考虑,做一次MST,得到一棵树T'
此时T' - S的边无论S中所有边边权如何改变,永远都会存在于MST中
因为这些边是优先S的情况下选择的,S无论如何改变,只能更劣,并不影响这些边的选择
现在,定义S = {边e∈修改[l,r]中影响的边},
那么每次,我们执行操作1,删掉那些永远不会被选的边,记这个操作为Reduction
然后对于剩下的边,执行操作2,强制加入所有必选边,或是说缩点,记这个操作为Contraction
那么每次,Reduction后,剩下的边的数量最坏是|S| + n - 1,就是G - S中的边能生成一棵树
而Contraction后,剩下的点的数量最坏是|S|,也就是|S|中的边刚好无环
整个过程放在分治里,每次|S|会缩小一半,因而操作1剩下的边数最坏约是3*S,因为n和上一层分治一个数量级
3已经可以看做是常数了,那么我们只要在分治的时候Reduction,Contraction,将新图传递给子孙解决
当l == r,剩下2~3个点,一些边,此处先修改,然后做一个MST,询问就解决了,
返回的时候定义操作Recover,能抵消Reduction-Contraction的操作就行了,
显然Recover的复杂度和Reduction-Contraction一个数量级
缩点苟蒻是用安轶合并的并查集,然后Solve(l,mid)返回时对边集重新排序下
因此总的复杂度是O(nlog^n)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
const int maxn = 2E4 + 20;
const int maxm = 5E4 + 50;
typedef long long LL;
struct E{
int x,y,w; E(){}
E(int x,int y,int w): x(x),y(y),w(w){}
}edgs[maxm];
struct data{
int x,y,l; data(){}
data(int x,int y,int l): x(x),y(y),l(l){}
}s[maxm];
int n,m,q,cnt,tp,L[maxn],Mark[maxm],po[maxm],d[maxm],fa[maxn],F[maxn];
LL Ans[maxm];
bool cmp(const int &x,const int &y) {return edgs[x].w < edgs[y].w;}
int getF(int x) {return x == F[x]?x:getF(F[x]);}
int getfa(int x) {return x == fa[x]?x:fa[x] = getfa(fa[x]);}
void Merge(int x,int y)
{
if (L[x] > L[y]) swap(x,y);
s[++tp] = data(x,y,L[y]);
F[x] = y; L[y] = L[x] == L[y]?L[x]+1:L[y];
}
void Reset_fa(vector <int> v)
{
for (int i = 0; i < v.size(); i++) {
E e = edgs[v[i]];
int x = getF(e.x),y = getF(e.y);
fa[x] = x; fa[y] = y;
}
}
void Reduction(vector <int> &v,vector <int> &g)
{
Reset_fa(v);
for (int i = 0; i < v.size(); i++)
if (Mark[v[i]] == cnt) g.push_back(v[i]);
else {
E e = edgs[v[i]];
int x = getF(e.x),y = getF(e.y);
int fx = getfa(x),fy = getfa(y);
if (fx == fy) continue;
fa[fx] = fy; g.push_back(v[i]);
}
}
void Contraction(vector <int> &g,vector <int> &t,LL &tot)
{
Reset_fa(g);
for (int i = 0; i < g.size(); i++) {
if (Mark[g[i]] != cnt) continue;
E e = edgs[g[i]];
int x = getF(e.x),y = getF(e.y);
int fx = getfa(x),fy = getfa(y);
if (fx == fy) continue; fa[fx] = fy;
}
for (int i = 0; i < g.size(); i++) {
if (Mark[g[i]] == cnt) {t.push_back(g[i]); continue;}
E e = edgs[g[i]];
int x = getF(e.x),y = getF(e.y);
int fx = getfa(x),fy = getfa(y);
if (fx == fy) {t.push_back(g[i]); continue;}
tot += 1LL*e.w;
Merge(x,y); fa[fx] = fy;
}
}
void Recover(int cur)
{
while (tp > cur) {
data t = s[tp--];
F[t.x] = t.x; L[t.y] = t.l;
}
}
void Solve(int l,int r,vector <int> &v,LL tot)
{
if (l == r) {
int cur = tp;
edgs[po[l]].w = d[l];
sort(v.begin(),v.end(),cmp);
for (int i = 0; i < v.size(); i++) {
E e = edgs[v[i]];
int fx = getF(e.x),fy = getF(e.y);
if (fx == fy) continue;
tot += 1LL*e.w; Merge(fx,fy);
}
Ans[l] = tot;
Recover(cur);
return;
}
++cnt; int mid = (l + r) >> 1;
for (int i = l; i <= r; i++) Mark[po[i]] = cnt;
vector <int> g,t; g.clear(); t.clear();
int cur = tp;
Reduction(v,g);
Contraction(g,t,tot);
Solve(l,mid,t,tot);
sort(t.begin(),t.end(),cmp);
Solve(mid+1,r,t,tot);
Recover(cur);
}
int main()
{
#ifdef DMC
freopen("DMC.txt","r",stdin);
#endif
cin >> n >> m >> q;
for (int i = 1; i <= m; i++) {
int x,y,w; scanf("%d%d%d",&x,&y,&w);
edgs[i] = E(x,y,w);
}
for (int i = 1; i <= n; i++) F[i] = i,L[i] = 1;
for (int i = 1; i <= q; i++) scanf("%d%d",&po[i],&d[i]);
vector <int> v; v.clear();
for (int i = 1; i <= m; i++) v.push_back(i);
sort(v.begin(),v.end(),cmp);
Solve(1,q,v,0);
for (int i = 1; i <= q; i++) printf("%lld\n",Ans[i]);
return 0;
}