• NOI 2001/POJ 1182 食物链


    边带权并查集/扩展域并查集例题
    【题意】
    动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。
    A吃B, B吃C,C吃A。
    现有N个动物,以1-N编号。
    每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
    有人用两种说法对这N个动物所构成的食物链关系进行描述:
    第一种说法是”1 X Y”,表示X和Y是同类。
    第二种说法是”2 X Y”,表示X吃Y。
    此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。
    当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
    1) 当前的话与前面的某些真的话冲突,就是假话;
    2) 当前的话中X或Y比N大,就是假话;
    3) 当前的话表示X吃X,就是假话。
    你的任务是根据给定的N和K句话,输出假话的总数。

    边带权并查集

    我们把环展开,可以发现深度与类别有一定的规律。
    在这里插入图片描述
    这里有向边x>yx->y,表示xyx吃y。根的深度为0。
    d[x]d[x]表示xx的深度。则有:

    1. d[x]mod  3=0,xd[x] mod 3 =0,x与根是同类。
    2. d[x]mod  3=1,xd[x] mod 3 =1,x吃根。
    3. d[x]mod  3=2,xd[x] mod 3 =2,根吃x。

    所以在判定假话时,x,yx,y在同一集合中的话。则有:

    1. (d[x]d[y])mod  3=0,xy(d[x]-d[y])mod 3 =0,x与y是同类
    2. (d[x]d[y])mod  3=1,xy(d[x]-d[y]) mod 3 =1,x吃y
    3. (d[x]d[y])mod  3=2,yx(d[x]-d[y]) mod 3 =2,y吃x

    这里再讲一下连接两个集合时需要注意的问题。
    DD表示x,yx,y的关系(读入后DD--),lenlen表示xyx的根与y的根连边的长度.
    (d[x]+lend[y])D(mod  3)(d[x]+len-d[y])equiv D ( mod 3)
    lenD(d[x]d[y])(mod  3)lenequiv D-(d[x]-d[y]) ( mod 3)

    代码:

    //边带权 
    #include<cstdio>
    #include<cctype>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #define g getchar()
    using namespace std;
    typedef long long ll;
    const int N=50010;
    template<class o>void qr(o&x) {
    	char c=g;x=0;int f=1;
    	while(!isdigit(c)){if(c=='-')f=-1;c=g;}
    	while(isdigit(c))x=x*10+c-'0',c=g;
    	x*=f;
    }
    void write(int x) {
    	if(x/10)write(x/10);
    	putchar(x%10+'0');
    }
    
    int n,m,fa[N],d[N],cnt;
    int findfa(int x) {
    	if( fa[x] == x ) return x;
    	int root = findfa(fa[x]);
    	d[x] += d[fa[x]];
    	return fa[x] = root;
    }
    int main() {
    	qr(n); qr(m); for(int i = 1;i<=n;i++) fa[i] = i;
    	while(m--) {
    		int D, x, y; qr(D); D--; qr(x); qr(y);
    		if( x > n || y > n || ( D == 1 && x == y ) ) { ++cnt; continue; }
    		int tx = findfa(x),ty = findfa(y);
    		d[x] = ( d[x] % 3 + 3) % 3;
    		d[y] = ( d[y] % 3 + 3) % 3;
    		int dis = ( (d[x] - d[y]) + 3 ) % 3;
    		//dis = 1表示x吃y,dis = 2表示y吃x,dis = 0表示x,y是同类 
    		if( tx == ty ) {
    			if( D != dis ) 
    				{ ++cnt; continue; } 
    		}
    		else fa[tx] = ty,d[tx] = ( ( D - ( d[x] - d[y] ) ) % 3 + 3 ) % 3;
    	}
    	write(cnt);puts("");return 0;
    }
    
    

    扩展域并查集:

    给每个点开三个点,表示同类、天敌、食物。

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<algorithm>
    #define g getchar()
    using namespace std;
    typedef long long ll;
    const int N=15e4+10;
    template<class o>void qr(o&x) {
    	char c=g;x=0;int f=1;
    	while(!isdigit(c)){if(c=='-')f=-1;c=g;}
    	while(isdigit(c))x=x*10+c-'0',c=g;
    	x*=f;
    }
    void write(int x) {
    	if(x/10)write(x/10);
    	putchar(x%10+'0');
    }
    
    int fa[N],n,m,ans;
    int findfa(int x) { return fa[x]==x?x:fa[x]=findfa(fa[x]);}
    int main() {
    	qr(n);qr(m);for(int i=1;i<=3*n;i++)fa[i]=i;
    	while(m--) {
    		int d,x,y;qr(d);qr(x);qr(y);
    		if(x>n||y>n||(d==2&&x==y)){ans++;continue;}
    		if(d==1) {//捕食、同类、天敌 
    			if(findfa(x)==findfa(y+n)||findfa(x)==findfa(y+2*n)) 
    				{ans++;continue;}
    			fa[findfa(x)]=findfa(y);
    			fa[findfa(x+n)]=findfa(y+n);
    			fa[findfa(x+2*n)]=findfa(y+2*n);
    		}
    		else {
    			if(findfa(x)==findfa(y)||findfa(x+n)==findfa(y)) 
    				{ans++;continue;}
    			fa[findfa(x)]=findfa(y+n);
    			fa[findfa(x+n)]=findfa(y+2*n);
    			fa[findfa(x+2*n)]=findfa(y);
    		}
    	}
    	write(ans);puts("");return 0;
    }
    

    总结:

    边带权并查集更加灵活,而扩展域并查集更加直观。

  • 相关阅读:
    EF 配置(SqlServer,Mysql)
    mysql sql优化
    非root用户安装、配置mysql
    使用spring jdbc遇到的一个性能问题
    mac 修改 vim 配色
    logstash 监控日志文件时应对日志文件名改变的原理
    java Atomic compareAndSet部分原理分析
    实现进程单例的一些想法
    java String、String.concat和StringBuilder性能对比
    Elasticsearch 动态修改replica配置、增删replica
  • 原文地址:https://www.cnblogs.com/zsyzlzy/p/12373882.html
Copyright © 2020-2023  润新知