zoukankan      html  css  js  c++  java
  • UVA

    题面

        感觉这个题还是蛮灵活的,并没有死考一个并查集。

        考虑如果没有2操作,那么这就是一个并查集的模板题。多出一个2操作增加了什么困难呢?

        如果我们直接用并查集维护,那么2操作必须改变f[p],这样的话,原来那些在p子树里的元素也就跟着一起被移到q所在集合里了。

        这显然是不对的。。。

        其实我们要做的仅仅是把p这个点扣出来,而尽量使p原来所在的集合不太变动。

        这么一想,就有了一个思路:每次进行2操作我们就新开一个点代表 新p,我们只需要把 f[新p] 设置成q的根,然后把原根的信息改一改,q的根的信息改一改就好了。对于1~n我们开一个数组记录它们的新点是多少,不管什么操作我们都用新点。

        新算法的正确性在于:每次2操作开一个新点之后,原点就变成了一个仅有指示集合作用的虚点,并且在后续操作中(除了原来指向它的点)不会被用到;并且本题求的值都可以直接记录在并查集的根上,所以操作2后改一改两个根的信息以后就一劳永逸了,不会再用到没改的小树的错误信息。。

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N=100005;
    
    int n,m,f[N*2],dy[N],num[N*2];
    ll sum[N*2];
    
    inline int read(){
        int x=0; char ch=getchar();
        for(;!isdigit(ch);ch=getchar());
        for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
        return x;
    }
    
    int getf(int x){ return f[x]==x?x:(f[x]=getf(f[x]));}
    
    inline void solve(){
    	for(int i=1;i<=n;i++) f[i]=dy[i]=i,num[i]=1,sum[i]=i;
    	
    	for(int opt,p,q;m;m--){
    		opt=read();
    		
    		if(opt==3) p=getf(dy[read()]),printf("%d %lld
    ",num[p],sum[p]);
    		
    		else if(opt==1){
    			p=getf(dy[read()]),q=getf(dy[read()]);
    			if(p!=q) f[p]=q,num[q]+=num[p],sum[q]+=sum[p];
    		}
    		
    		else{
    			p=read(),q=getf(dy[read()]);
    			int fa=getf(dy[p]);
    			if(fa==q) continue;
    			
    			num[fa]--,sum[fa]-=(ll)p;
    			dy[p]=++n,f[n]=q,num[q]++,sum[q]+=(ll)p;
    		}
    	}
    }
    
    int main(){
    	while(scanf("%d%d",&n,&m)==2) solve();
    	return 0;
    }
    

      

  • 相关阅读:
    如何插入和查找记录(行)(十一)
    如何查看数据表及数据表结构(十)
    如何创建数据表(九)
    MySQL常见的数据类型(八)
    MySQL数据库的常见操作(七)
    如何修改提示符(六)
    MySQL的登录和退出(五)
    如何启动和关闭MySQL?(四)
    如何配置MySQL?(三)
    如何安装MySQL?(二)
  • 原文地址:https://www.cnblogs.com/JYYHH/p/11274848.html
Copyright © 2011-2022 走看看