zoukankan      html  css  js  c++  java
  • BZOJ2594 [Wc2006]水管局长数据加强版 LCT kruskal

    欢迎访问~原文出处——博客园-zhouzhendong

    去博客园看该题解


    题目传送门 - BZOJ2594


    题意概括

      N个点的图,M条带权边。(N<=100000,M<=1000000)

      有Q次操作(Q<=100000)

      操作有两个类型:

      1.问节点x到y的路径中边的最大权值。

      2.删除某一条边

      操作过程中保证图连通


    题解

      我们发现很难做。

      能够1A也是我运气好。

      我们发现顺着做貌似很难,要找到边,然后删掉……

      这题是LCT的,显然不会在图上做。

      看了看网上的题解,懂了。

      倒着来,把删边看作加边。

      假如没有删边或者加边的操作,那么有用的边一定是最小生成树上的。

      换句话说就是a~b中路径的最大权值最小的边一定在当前最小生成树上。

      于是我们倒着维护一棵最小生成树。

      每次加入一条边,连接x和y,权值为z,我们要找到x和y的路径中权值最大的边。

      然后把他删掉,再添入当前边(当然这个权值最大的可能是他自己,那么什么都不干了)。

      这个贪心的思想应该不用讲了吧。

      现在是关键是怎么做。

      我们考虑用LCT来维护。

      但是LCT只可以维护点权,很难做到维护边权。

      所以我们化边为点。

      我们把每一条边看作一个点。

      然后比如说第k条边连接了x和y,那么连上第k条边的时候,我们就分别link(x,k)和link(y,k)

      然后就达到了连接点的同样的效果。

      那么,再写一个LCT维护一下最大值就可以了。

      这里有一个小技巧,就是记录最大值编号。

      还有一个查询边的问题,那么我们可以给边按照节点排个序,然后二分查找即可。

      这个可以事先处理好。

      详见代码


    代码

    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <cstdlib>
    #include <cmath>
    using namespace std;
    const int N=100005,M=1000005,S=N+M;
    bool isd(char ch){
    	return '0'<=ch&&ch<='9';
    }
    void read(int &x){
    	x=0;
    	char ch=getchar();
    	while (!isd(ch))
    		ch=getchar();
    	while (isd(ch))
    		x=x*10+ch-48,ch=getchar();
    }
    int n,m,Q,s;
    int fa[S],son[S][2],rev[S],val[S],Max[S];
    int ans[N];
    struct Three{
    	int x,y,z,flag;
    	void Read(int flag){
    		read(x),read(y),read(z);
    		if (flag&&x>y)
    			swap(x,y);
    		flag=0;
    	}
    }e[M],q[N];
    bool cmp1(Three a,Three b){
    	return (a.x==b.x&&a.y<b.y)||a.x<b.x;
    }
    bool cmp2(Three a,Three b){
    	return a.z<b.z;
    }
    void clear(int x,int v){
    	fa[x]=son[x][0]=son[x][1]=rev[x]=0;
    	val[x]=Max[x]=v;
    }
    int search(int a,int b){
    	if (a>b)
    		swap(a,b);
    	int L=1,R=m,mid;
    	while (L<=R){
    		mid=(L+R)>>1;
    		if (e[mid].x==a&&e[mid].y==b)
    			return mid;
    		if ((e[mid].x==a&&e[mid].y<b)||e[mid].x<a)
    			L=mid+1;
    		else
    			R=mid-1;
    	}
    }
    bool isroot(int x){
    	return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;
    }
    void pushup(int x){
    	int LMax=Max[son[x][0]],RMax=Max[son[x][1]];
    	Max[x]=e[LMax].z>e[RMax].z?LMax:RMax;
    	Max[x]=e[val[x]].z>e[Max[x]].z?val[x]:Max[x];
    }
    void pushdown(int x){
    	if (rev[x]){
    		rev[x]=0;
    		rev[son[x][0]]^=1;
    		rev[son[x][1]]^=1;
    		swap(son[x][0],son[x][1]);
    	}
    }
    void pushadd(int x){
    	if (!isroot(x))
    		pushadd(fa[x]);
    	pushdown(x);
    }
    int wson(int x){
    	return son[fa[x]][1]==x;
    }
    void rotate(int x){
    	if (isroot(x))
    		return;
    	int y=fa[x],z=fa[y],L=wson(x),R=L^1;
    	if (!isroot(y))
    		son[z][wson(y)]=x;
    	fa[x]=z,fa[y]=x,fa[son[x][R]]=y;
    	son[y][L]=son[x][R],son[x][R]=y;
    	pushup(y),pushup(x);
    }
    void splay(int x){
    	pushadd(x);
    	for (int y=fa[x];!isroot(x);rotate(x),y=fa[x])
    		if (!isroot(y))
    			rotate(wson(x)==wson(y)?y:x);
    }
    void access(int x){
    	int t=0;
    	while (x){
    		splay(x);
    		son[x][1]=t;
    		pushup(x);
    		t=x;
    		x=fa[x];
    	}
    }
    void rever(int x){
    	access(x);
    	splay(x);
    	rev[x]^=1;
    }
    void link(int x,int y){
    	rever(x);
    	fa[x]=y;
    }
    void split(int x,int y){//让y做x的左儿子 
    	rever(y);
    	access(x);
    	splay(x);
    }
    void cut(int x,int y){
    	split(y,x);
    	fa[x]=son[y][0]=0;
    }
    int find(int x){
    	access(x);
    	splay(x);
    	while (1){
    		pushdown(x);
    		if (son[x][0])
    			x=son[x][0];
    		else
    			break;
    	}
    	return x;
    }
    int main(){
    	read(n),read(m),read(Q);
    	for (int i=1;i<=m;i++)
    		e[i].Read(1);
    	for (int i=1;i<=Q;i++)
    		q[i].Read(0);
    	sort(e+1,e+m+1,cmp1);
    	for (int i=1;i<=Q;i++)
    		if (q[i].x==2)
    			e[search(q[i].y,q[i].z)].flag=i;
    	sort(e+1,e+m+1,cmp2);
    	for (int i=1;i<=m;i++)
    		if (e[i].flag)
    			q[e[i].flag].flag=i;
    	s=n+m;
    	for (int i=1;i<=s;i++)
    		clear(i,i<=n?0:(i-n));
    	e[0].z=0;
    	for (int i=1,j=1;i<=m&&j<n;i++){
    		if (e[i].flag)
    			continue;
    		int x=e[i].x,y=e[i].y;
    		if (find(x)==find(y))
    			continue;
    		link(x,i+n);
    		link(y,i+n);
    		j++;
    	}
    	for (int i=Q;i>=1;i--){
    		int k=q[i].x,x=q[i].y,y=q[i].z;
    		if (k==1){
    			split(x,y);
    			ans[i]=e[Max[x]].z;
    		}
    		if (k==2){
    			split(x,y);
    			int cE=Max[x],aE=q[i].flag;
    			if (e[cE].z>e[aE].z){
    				cut(e[cE].x,cE+n);
    				cut(e[cE].y,cE+n);
    				link(e[aE].x,aE+n);
    				link(e[aE].y,aE+n);
    			}
    		}
    	}
    	for (int i=1;i<=Q;i++)
    		if (q[i].x==1)
    			printf("%d
    ",ans[i]);
    	return 0;
    }
    

      

  • 相关阅读:
    MongoDB 创建数据库
    MongoDB
    MongoDB 概念解析
    window平台安装 MongoDB(二)
    MongoDB入门学习(1)
    解决DevExpress10.2.4版本在VS2012工具箱控件不显示的问题
    Aspose.Word 输出表格后空格字符丢失的解决方法
    ArcEngine 创建空间参考设置默认域
    SPATIALITE 各版本数据库差异
    WGS84投影的WKID说明
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ2594.html
Copyright © 2011-2022 走看看