bzoj2001【HNOI2010】City 城市建设

2001: [Hnoi2010]City 城市建设

Time Limit: 20 Sec   Memory Limit: 162 MB
Submit: 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

5 5 3
1 2 1
2 3 2
3 4 3
4 5 4
5 1 5
1 6
1 1
5 3

Sample Output

14
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;
}


你可能感兴趣的:(最小生成树,bzoj,cdq分治)