• BZOJ2001 [Hnoi2010]City 城市建设


    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

     

    本文作者:ljh2000
    作者博客:http://www.cnblogs.com/ljh2000-jump/
    转载请注明出处,侵权必究,保留最终解释权!


    题目链接:BZOJ2001

    正解:$CDQ$分治+并查集

    解题报告:

      这道题的$CDQ$分治思想非常巧妙…

      考虑我在处理区间$[l,r]$时的情况,我把在这一段区间中会被修改的边称为特殊边,

      我先把特殊边的权值设为$-inf$,跑一遍$MST$,此时在$MST$中的非特殊边是必然会在这一段区间中的任何时候被选择的,所以我把他连接的两个点合并起来变成一个点,降低图的规模。

      我再把特殊边的权值设为$inf$,跑一遍$MST$,那么此时没在图中的边在这整个区间中一定也不在$MST$中,所以可以去掉。

      然后递归做$[l,mid]$、$[mid+1,r]$就好了。

      具体实现有点麻烦、复杂,可以看看代码。

      注意清空并查集的时候设置上限,保证复杂度。另外,每次只需要处理$[l,r]$范围内的修改!

    //It is made by ljh2000
    //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <ctime>
    #include <vector>
    #include <queue>
    #include <map>
    #include <set>
    #include <string>
    #include <complex>
    #include <bitset>
    using namespace std;
    typedef long long LL;
    typedef long double LB;
    typedef complex<double> C;
    const double pi = acos(-1);
    const int MAXN = 50011;
    const int MAXM = 50011;
    const int inf = (1<<30)-1;
    int n,m,Q,val[MAXM],id[MAXM],gn[17],gm[17],bel[MAXN];
    LL ans[MAXN];
    bool use[MAXM];
    struct edge{ int x,y,z,id; }e[17][MAXM],tmp[MAXM],wyh[MAXM];//每层的边单独存
    inline bool cmp(edge q,edge qq){ return q.z<qq.z; }
    struct ask{ int id,z; }q[MAXN];
    
    inline int getint(){
        int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
        if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
    }
    
    namespace Union_set{
    	int father[MAXN],size[MAXN];
    	inline void init(int x){ for(int i=1;i<=x;i++) father[i]=i,size[i]=1; }
    	inline int find(int x){ if(father[x]!=x) father[x]=find(father[x]); return father[x]; }
    	inline bool merge(int x,int y){
    		x=find(x); y=find(y);
    		if(x==y) return 0;
    		if(size[x]>size[y]) swap(x,y);
    		father[x]=y; size[y]+=size[x];
    		return 1;
    	}
    }
    
    using namespace Union_set;
    
    inline void rebuild(int &N,int &M,LL &cost){//把MST中的必然存在的边,所连接的点合并
    	init(N); int nn=0,nm=0;
    	sort(tmp+1,tmp+M+1,cmp); 
    	for(int i=1;i<=M;i++) use[i]=0;//记录是否被合并
    	for(int i=1;i<=M;i++) {
    		if(merge(tmp[i].x,tmp[i].y) && tmp[i].z!=(-inf) ) use[i]=1,cost+=tmp[i].z;
    		else wyh[++nm]=tmp[i];
    	}
    	init(N);
    	for(int i=1;i<=M;i++) if(use[i]) merge(tmp[i].x,tmp[i].y);
    	for(int i=1;i<=N;i++) if(find(i)==i) bel[i]=++nn;
    	for(int i=1;i<=N;i++) bel[i]=bel[find(i)];
    	for(int i=1;i<=nm;i++) {
    		tmp[i]=wyh[i];
    		id[tmp[i].id]=i;
    		tmp[i].x=bel[tmp[i].x]; tmp[i].y=bel[tmp[i].y];
    	}
    	N=nn; M=nm;
    }
    
    inline void rebuild2(int &N,int &M){//把MST中不可能出现的边去掉
    	init(N); int nm=0;
    	sort(tmp+1,tmp+M+1,cmp);
    	for(int i=1;i<=M;i++) 
    		if(merge(tmp[i].x,tmp[i].y) || tmp[i].z==inf)
    			tmp[++nm]=tmp[i],id[ tmp[i].id ]=nm;
    	M=nm;
    }
    
    inline void CDQ(int l,int r,int ceng,LL cost){
    	int N=gn[ceng],M=gm[ceng];
    	if(l==r) val[q[l].id]=q[l].z;
    	for(int i=1;i<=M;i++) {
    		e[ceng][i].z=val[ e[ceng][i].id ];
    		tmp[i]=e[ceng][i];
    		id[tmp[i].id]=i;//记录每条边的出现编号
    	}
    	init(N);
    	if(l==r) {//直接做一遍最小生成树
    		sort(e[ceng]+1,e[ceng]+M+1,cmp);
    		for(int i=1;i<=M;i++)
    			if(merge(e[ceng][i].x,e[ceng][i].y))
    				cost+=e[ceng][i].z;
    		ans[l]=cost;
    		return ;
    	}
    
    	int mid=(l+r)>>1;
    	//只考虑[l,r]范围内的!!!
    	for(int i=l;i<=r;i++) tmp[ id[ q[i].id ] ].z=-inf;
    	rebuild(N,M,cost);
    	for(int i=l;i<=r;i++) tmp[ id[ q[i].id ] ].z=inf;
    	rebuild2(N,M);
    
    	gn[ceng+1]=N;
    	gm[ceng+1]=M;
    	for(int i=1;i<=M;i++) e[ceng+1][i]=tmp[i];
    	CDQ(l,mid,ceng+1,cost); 
    	CDQ(mid+1,r,ceng+1,cost);
    }
    
    inline void work(){
    	n=getint(); m=getint(); Q=getint();
    	for(int i=1;i<=m;i++) {
    		e[0][i].x=getint(); e[0][i].y=getint(); e[0][i].z=getint();
    		e[0][i].id=i; val[i]=e[0][i].z;
    	}
    
    	for(int i=1;i<=Q;i++)
    		q[i].id=getint(),q[i].z=getint();
    
    	gn[0]=n; gm[0]=m;
    	CDQ(1,Q,0,0);
    
    	for(int i=1;i<=Q;i++)
    		printf("%lld
    ",ans[i]);
    }
    
    int main()
    {
        work();
        return 0;
    }
    //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
    

      

  • 相关阅读:
    我修改/收藏的CSDN知识.(asp.net JavaScript)
    哪里摔倒就在哪里躺下
    显示存储过程的名称、创建时间、修改时间
    Flash Builder 初试(二)绑定和双向绑定
    C#支持中文的格式化字符长度方法
    Flash Builder 初试(一)信息提示窗口
    Flash Builder 初试(三) 使用摄像头
    Null Object 模式
    开放封闭原则(OCP)
    面向对象设计5大准则
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6527342.html
Copyright © 2020-2023  润新知