zoukankan      html  css  js  c++  java
  • 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
    ",ans[i]);
    }
    
    int main()
    {
    	work();
    	return 0;
    }
    
    
  • 相关阅读:
    老杆子遇到新问题
    Linux 下网路适配器配置
    OpenCV学习笔记2_ShowAvi_获取Avi视频图像、摄像头图像
    OpenCV学习笔记3_ShowAvi_Trackbar_加载视频,并添加拖动条
    PS照片
    OpenCV学习笔记7_ImageToBGR_彩色图像三通道转化成BGR3幅单通道图像
    #include "stdafx.h"_预编译头文件
    OpenCV学习笔记1_ShowImage_显示一幅图像
    Visual Assist X_VS2010编程插件安装
    OpenCV学习笔记4_ImageToAvi_写视频
  • 原文地址:https://www.cnblogs.com/Yuzao/p/7968065.html
Copyright © 2011-2022 走看看