bzoj 2001: [Hnoi2010]City 城市建设

Description

PS国是一个拥有诸多城市的大国,国王Louis为城市的交通建设可谓绞尽脑汁。Louis可以在某些城市之间修建道路,在不同的城市之间修建道路需要不同的花费。Louis希望建造最少的道路使得国内所有的城市连通。但是由于某些因素,城市之间修建道路需要的花费会随着时间而改变,Louis会不断得到某道路的修建代价改变的消息,他希望每得到一条消息后能立即知道使城市连通的最小花费总和, Louis决定求助于你来完成这个任务。

solution

正解:CDQ分治
很久以前看过题解,一直没敢写,思路就是利用分治狂加剪枝
设 solve(l,r) 表示处理第[l,r]个询问
两个剪枝:
1.删除不可能出现在生成树中的边
2.直接加上必然出现在生成树中的边,并将并查集做永久修改

剪枝1:

把要修改的边改成-inf,然后做最小生成树,保留非inf边,因为就算这些待修改的边经过改变都变得更优,这些边也可以被选到,所以是必然出现的,我们把这些非inf边加入贡献,并把对应的连缩起来

剪枝2:

把要修改的边改成 inf,然后做最小生成树,去掉不在生成树中的边,因为就算不用这些待修改的边,也不会用到,所以直接去掉即可
这样做的复杂度是对的,第一个操作把点数缩到了询问区间的长度,第二个操作把边数缩到了点数,所以每次边数随区间长度除以2

注意一些细节:比如并查集初始化时,只能改当前的生成树,避免对之前的永久化修改产生影响

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
using namespace std;
typedef long long ll;
const int N=50005,inf=2e8+41;
struct node{
	int x,y,z,id;
	bool operator <(const node &pr)const{return z<pr.z;}
}e[22][N],t[N],kt[N];
struct Plan{int x,y;}h[N];
int n,m,sum[41],rk[N],fa[N],res[N];ll ans[N];
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void build(int &tot,ll &re){
	for(int i=1;i<=tot;i++)fa[t[i].x]=t[i].x,fa[t[i].y]=t[i].y;
	sort(t+1,t+tot+1);
	int x,y,l=0;
	for(int i=1;i<=tot;i++){
		x=t[i].x;y=t[i].y;
		if(find(x)==find(y))continue;
		kt[++l]=t[i];fa[find(y)]=find(x);
	}
	for(int i=1;i<=l;i++)fa[kt[i].x]=kt[i].x,fa[kt[i].y]=kt[i].y;
	for(int i=1;i<=l;i++)
		if(kt[i].z!=-inf) 
                  re+=kt[i].z,fa[find(kt[i].y)]=find(kt[i].x);
	l=0;
	for(int i=1;i<=tot;i++){
		x=t[i].x;y=t[i].y;
		if(find(x)==find(y))continue;
		kt[++l]=t[i];rk[t[i].id]=l;
		kt[l].x=find(x);
		kt[l].y=find(y);
	}
	tot=l;for(int i=1;i<=l;i++)t[i]=kt[i];
}
inline void delet(int &tot){
	for(int i=1;i<=tot;i++)fa[t[i].x]=t[i].x,fa[t[i].y]=t[i].y;
	sort(t+1,t+tot+1);
	int x,y,l=0;
	for(int i=1;i<=tot;i++){
		x=t[i].x;y=t[i].y;
		if(find(x)==find(y)){
			if(t[i].z==inf)kt[++l]=t[i];
			continue;
		}
		kt[++l]=t[i];
		fa[find(y)]=find(x);
	}
	tot=l;for(int i=1;i<=l;i++)t[i]=kt[i];
}
inline void solve(int l,int r,int d,ll re){
	if(l==r)res[h[l].x]=h[l].y;
	int tot=sum[d];
	for(int i=1;i<=tot;i++)
         t[i]=e[d][i],t[i].z=res[t[i].id],rk[t[i].id]=i;

	if(l==r){
		for(int i=1;i<=tot;i++)
                    fa[t[i].x]=t[i].x,fa[t[i].y]=t[i].y;
		sort(t+1,t+tot+1);
		int x,y;
		for(int i=1;i<=tot;i++){
			x=t[i].x;y=t[i].y;
			if(find(x)==find(y))continue;
			ans[l]+=t[i].z;fa[find(y)]=find(x);
		}
		ans[l]+=re;
		return ;
	}
	
	for(int i=l;i<=r;i++)t[rk[h[i].x]].z=-inf;
	build(tot,re);
	for(int i=l;i<=r;i++)t[rk[h[i].x]].z=inf;
	delet(tot);

	sum[d+1]=tot;for(int i=1;i<=tot;i++)e[d+1][i]=t[i];
	
	int mid=(l+r)>>1;
	solve(l,mid,d+1,re);solve(mid+1,r,d+1,re);
}

void work()
{
	int Q;
	scanf("%d%d%d",&n,&m,&Q);
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&e[0][i].x,&e[0][i].y,&e[0][i].z);
		e[0][i].id=i;res[i]=e[0][i].z;
	}
   for(int i=1;i<=Q;i++)scanf("%d%d",&h[i].x,&h[i].y);
	sum[0]=m;solve(1,Q,0,0);
	for(int i=1;i<=Q;i++)printf("%lld\n",ans[i]);
}

int main()
{
	work();
	return 0;
}

posted @ 2017-12-04 00:03  PIPIBoss  阅读(260)  评论(0编辑  收藏  举报