2001: [Hnoi2010]City 城市建设
Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 983 Solved: 473
[ 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。
CDQ分治+最小生成树,思路很棒
有两个关键操作:
Reduction(删除无用边):将待修改的边权值改为INF,做一遍MST,将所有权值不等于INF且不在MST中的边删掉。因为MST将来只可能变得更小,这些边一定不会有用。
Construction(缩必须边):将待修改的边权值改为-INF,做一遍MST,将所有权值不等于-INF且在MST中的边称为必须边,对于必须边缩点。因为不管待修改的边权值改为多少,这些边都是必须选的。
每次分治操作,先进行删边和缩点两个操作,然后分治下面一层,分治到最底层的时候修改边权并且计算答案。
然后就是一些细节了,这道题写起来挺麻烦的。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 20010
#define maxm 50010
#define inf 1000000000
using namespace std;
int n,m,Q,sum[25],f[maxn],size[maxn],c[maxm],a[maxm];
ll ans[maxn];
struct edge{int x,y,w,pos;}e[25][maxm],d[maxm],t[maxm];
struct data{int x,y;}q[maxm];
inline int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline bool cmp(edge a,edge b){return a.w<b.w;}
inline int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
inline void merge(int x,int y)
{
if (size[x]<=size[y]) size[y]+=size[x],f[x]=y;
else size[x]+=size[y],f[y]=x;
}
inline void clear(int tot)
{
F(i,1,tot)
{
f[d[i].x]=d[i].x;f[d[i].y]=d[i].y;
size[d[i].x]=size[d[i].y]=1;
}
}
void constraction(int &tot,ll &cnt)
{
int tmp=0;
clear(tot);
sort(d+1,d+tot+1,cmp);
F(i,1,tot)
{
int fx=find(d[i].x),fy=find(d[i].y);
if (fx!=fy) merge(fx,fy),t[++tmp]=d[i];
}
F(i,1,tmp)
{
f[t[i].x]=t[i].x;f[t[i].y]=t[i].y;
size[t[i].x]=size[t[i].y]=1;
}
F(i,1,tmp)
{
int fx=find(t[i].x),fy=find(t[i].y);
if (t[i].w!=-inf&&fx!=fy) merge(fx,fy),cnt+=t[i].w;
}
tmp=0;
F(i,1,tot) if (find(d[i].x)!=find(d[i].y))
{
t[++tmp]=d[i];c[d[i].pos]=tmp;
t[tmp].x=f[d[i].x];t[tmp].y=f[d[i].y];
}
tot=tmp;F(i,1,tot) d[i]=t[i];
}
void reduction(int &tot)
{
int tmp=0;
clear(tot);
sort(d+1,d+tot+1,cmp);
F(i,1,tot)
{
int fx=find(d[i].x),fy=find(d[i].y);
if (fx!=fy) merge(fx,fy),t[++tmp]=d[i],c[d[i].pos]=tmp;
else if (d[i].w==inf) t[++tmp]=d[i],c[d[i].pos]=tmp;
}
tot=tmp;F(i,1,tot) d[i]=t[i];
}
void solve(int l,int r,int now,ll cnt)
{
int tot=sum[now];
if (l==r) a[q[l].x]=q[l].y;
F(i,1,tot) e[now][i].w=a[e[now][i].pos];
F(i,1,tot) d[i]=e[now][i],c[d[i].pos]=i;
if (l==r)
{
ans[l]=cnt;
clear(tot);
sort(d+1,d+tot+1,cmp);
F(i,1,tot)
{
int fx=find(d[i].x),fy=find(d[i].y);
if (fx!=fy) merge(fx,fy),ans[l]+=d[i].w;
}
return;
}
F(i,l,r) d[c[q[i].x]].w=-inf;
constraction(tot,cnt);
F(i,l,r) d[c[q[i].x]].w=inf;
reduction(tot);
F(i,1,tot) e[now+1][i]=d[i];
sum[now+1]=tot;
int mid=(l+r)>>1;
solve(l,mid,now+1,cnt);solve(mid+1,r,now+1,cnt);
}
int main()
{
n=read();m=read();Q=read();
int x,y;
F(i,1,m){e[0][i].x=read();e[0][i].y=read();e[0][i].w=a[i]=read();e[0][i].pos=i;}
F(i,1,Q) q[i].x=read(),q[i].y=read();
sum[0]=m;
solve(1,Q,0,0);
F(i,1,Q) printf("%lld\n",ans[i]);
return 0;
}