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;
    }
    

      

  • 相关阅读:
    Nios学习笔记3——流水灯实验
    Nios学习笔记2——流水灯实验
    Nios学习笔记1——流水灯实验
    转:摄像头camera 7660/7670/7225/9650以及程序流程(一)
    fpga 扇入扇出
    门控时钟的使用
    门控时钟与多扇出问题解决方案
    为所欲为——教你什么才是真正的任意分频
    SDRAM时序--读高手进阶,终极内存技术指南
    FPGA你必须知道的那些事儿
  • 原文地址:https://www.cnblogs.com/JYYHH/p/11274848.html
Copyright © 2011-2022 走看看