zoukankan      html  css  js  c++  java
  • [BZOJ 2959] 长跑(LCT+并查集)

    [BZOJ 2959] 长跑(LCT+并查集)

    题面

      为了让同学们更好地监督自己,学校推行了刷卡机制。
      学校中有n个地点,用1到n的整数表示,每个地点设有若干个刷卡机。
      有以下三类事件:
      1、修建了一条连接A地点和B地点的跑道。
      2、A点的刷卡机台数变为了B。
      3、进行了一次长跑。问一个同学从A出发,最后到达B最多可以刷卡多少次。具体的要求如下:
      当同学到达一个地点时,他可以在这里的每一台刷卡机上都刷卡。但每台刷卡机只能刷卡一次,即使多次到达同一地点也不能多次刷卡。
      为了安全起见,每条跑道都需要设定一个方向,这条跑道只能按照这个方向单向通行。最多的刷卡次数即为在任意设定跑道方向,按照任意路径从A地点到B地点能刷卡的最多次数。

    分析

    容易发现,如果图中有一个e-DCC,那么这上面的所有机器都可以被经过,贡献为所有点刷卡机台数的最大值。因此我们考虑动态维护e-DCC.

    对于每个e-DCC,我们任意选一个其中的点做代表元,缩成的点的编号就是代表元的编号,权值为所有点权值的最大值。那么怎么动态修改这棵树呢?

    首先,可以用并查集维护两个信息:

    1. 连通性

    2. 该点所在的e-DCC代表元,记为bel[x]

    第1个并查集判断是否无解用。

    第2个并查集需要和LCT的辅助树Splay结合在一起。加入一条边(x,y)时,如果(bel[x],bel[y])在原树上连通,就会产生一个e-DCC.缩点的时候我们先split出链(bel[x],bel[y])。然后暴力dfs这条链,把链上节点的bel改成bel[y].接着断开链上除了y的其他节点,直接lson(y)=0即可。根据均摊分析,复杂度不会超限。

    在Splay维护(如rotate等)时,我们把原来的Splay模板里的fa(x)改成x所在v-DCC的代表元的父亲。比如我们的rotate可以改写成这样。

    int find_bel(int x){
    		if(bel[x]==x) return x;
    		else return bel[x]=find_bel(bel[x]);
    	} 
    void rotate(int x){
    		int y=find_bel(fa(x)),z=find_bel(fa(y)),k=check(x),w=tree[x].ch[k^1];
    		tree[y].ch[k]=w;
    		tree[w].fa=y;
    		if(!is_root(y)) tree[z].ch[check(y)]=x;
    		tree[x].fa=z;
    		tree[x].ch[k^1]=y;
    		tree[y].fa=x;
    		push_up(y);
    		push_up(x);
    	}
    

    细节比较多,详情见代码。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxn 750000
    using namespace std;
    int n,m;
    inline void qread(int &x){
    	x=0;
    	int sign=1;
    	char c=getchar();
    	while(c<'0'||c>'9'){
    		if(c=='-') sign=-1;
    		c=getchar();
    	}
    	while(c>='0'&&c<='9'){
    		x=x*10+c-'0';
    		c=getchar();
    	} 
    	x=x*sign;
    }
    inline void qprint(int x){
    	if(x<0){
    		putchar('-');
    		qprint(-x);
    	}else if(x==0){
    		putchar('0');
    		return;
    	}else{
    		if(x>=10) qprint(x/10);
    		putchar(x%10+'0');
    	}
    }
    
    
    struct LCT{
    #define fa(x) (tree[x].fa)
    #define lson(x) (tree[x].ch[0])
    #define rson(x) (tree[x].ch[1])
    	struct node{
    		int ch[2];
    		int fa;
    		int revm;
    		int val;
    		int sum;
    	}tree[maxn+5];
    	int bel[maxn+5];
    	//并查集维护双连通分量关系,这里不能直接用Splay的fa(虽然也要存储);
    	//选联通分量中的一个节点作为代表节点 
    	//LCT模板里的fa(x)用find_bel(fa(x))代替,相当于每次跳到fa(x)所在e-DCC的代表节点 
    	//如果两个点的fa不相等,则说明不在一个e-DCC中 
    	int find_bel(int x){
    		if(bel[x]==x) return x;
    		else return bel[x]=find_bel(bel[x]);
    	} 
    	int con_id[maxn+5]; //并查集维护连通性
    	int find_con(int x){
    		if(con_id[x]==x) return x;
    		else return con_id[x]=find_con(con_id[x]);
    	}
    	void merge_con(int x,int y){
    		con_id[find_con(x)]=find_con(y);
    	}
    	
    	void push_up(int x){
    		tree[x].sum=tree[lson(x)].sum+tree[rson(x)].sum+tree[x].val;
    	}
    	inline bool is_root(int x){
    		return !(lson(find_bel(fa(x)))==x||rson(find_bel(fa(x)))==x);
    	}
    	int check(int x){
    		return rson(find_bel(fa(x)))==x;
    	}
    	void reverse(int x){
    		swap(lson(x),rson(x));
    		tree[x].revm^=1;
    	}
    	void push_down(int x){
    		if(tree[x].revm){
    			reverse(lson(x));
    			reverse(rson(x));
    			tree[x].revm=0;
    		}
    	}
    	void push_down_all(int x){
    		if(!is_root(x)) push_down_all(find_bel(fa(x)));
    		push_down(x);
    	}
    	void rotate(int x){
    		int y=find_bel(fa(x)),z=find_bel(fa(y)),k=check(x),w=tree[x].ch[k^1];
    		tree[y].ch[k]=w;
    		tree[w].fa=y;
    		if(!is_root(y)) tree[z].ch[check(y)]=x;
    		tree[x].fa=z;
    		tree[x].ch[k^1]=y;
    		tree[y].fa=x;
    		push_up(y);
    		push_up(x);
    	}
    	void splay(int x){
    		push_down_all(x);
    		while(!is_root(x)){
    			int y=find_bel(fa(x));
    			if(!is_root(y)){
    				if(check(x)==check(y)) rotate(y);
    				else rotate(x);
    			}
    			rotate(x);
    		}
    		push_up(x);
    	}
    	void access(int x){
    		for(int y=0;x;y=x,x=find_bel(fa(x))){
    			splay(x);
    			rson(x)=y;
    			push_up(x);
    		}
    	}
    	void make_root(int x){
    		access(x);
    		splay(x);
    		reverse(x);
    	}
    	void split(int x,int y){
    		make_root(x);
    		access(y);
    		splay(y);
    	}
    	void link(int x,int y){
    		make_root(x);
    		fa(x)=y;
    	}
    	void cut(int x,int y){
    		split(x,y);
    		fa(x)=lson(y)=0;
    		push_up(y);
    	}
    
    	void update_val(int x,int val){
    		make_root(x);
    		tree[x].val+=val;
    		tree[x].sum+=val;
    	}
    	void dfs_tree(int x,int y){
    		bel[x]=y;
    		push_down(x);
    		if(lson(x)) dfs_tree(lson(x),y);
    		if(rson(x)) dfs_tree(rson(x),y);
    	}
    	void shrink(int x,int y){
    		split(x,y);
    		tree[y].val=tree[y].sum;//一个点y相当于整个联通块 
    		dfs_tree(y,y);//把这棵树(整个联通块)里的节点全部指向y 
    		lson(y)=0;
    	}
    	int query_sum(int x,int y){
    		split(x,y);
    		return tree[y].sum;
    	}
    }T;
    
    
    
    //int bel[maxn+5];
    int a[maxn+5];
    int main(){
    //#ifdef LOCAL
    //	freopen("1.in","r",stdin);
    //	freopen("1.ans","w",stdout);
    //#endif
    	int type,x,y;
    	qread(n);
    	qread(m);
    	for(int i=1;i<=n;i++){
    		qread(a[i]); 
    		T.tree[i].val=a[i];
    		T.bel[i]=T.con_id[i]=i;
    	}
    //	for(int i=1;i<=n;i++) bel[i]=i;
    
    	for(int i=1;i<=m;i++){
    		qread(type);
    		qread(x);
    		qread(y);
    		if(type==1){
    			int fx=T.find_bel(x);
    			int fy=T.find_bel(y);
    			if(fx!=fy){//不在一个e-DCC中 
    				if(T.find_con(fx)!=T.find_con(fy)){//不连通,直接连边 
    					T.link(fx,fy);
    					T.merge_con(fx,fy); 
    				}else{//缩点 
    					T.shrink(fx,fy);
    				}
    			}
    
    		}else if(type==2){
    			int fx=T.find_bel(x);
    			T.update_val(fx,y-a[x]);
    			a[x]=y;
    		}else{
    			int fx=T.find_bel(x);
    			int fy=T.find_bel(y);
    			if(T.find_con(fx)==T.find_con(fy)) qprint(T.query_sum(fx,fy));
    			else qprint(-1);
    			putchar('
    ');
    		}
    	}
    }
    
  • 相关阅读:
    判断目录下的文件是否为图片
    多个视图结果显示于一个共用预览视图内
    最原创的验证码产生过程,桃花朵朵开
    ASP.NET MVC实现剪切图片
    ASP.NET MVC应用程序把文字写在图片上
    MVC把随机产生的字符串转换为图片
    ASP.NET MVC验证码演示(Ver2)
    ASP.NET MVC验证码演示
    linux负载均衡总结性说明(四层负载/七层负载)
    MySQL 优化系列(1)-- InnoDB重要参数优化
  • 原文地址:https://www.cnblogs.com/birchtree/p/12088770.html
Copyright © 2011-2022 走看看