zoukankan      html  css  js  c++  java
  • POJ 1733 Parity game

    前言

    并查集是一种维护可传递关系的有力数据结构!


    分析

    值域那么大,所以明显要用离散化。

    sumsum数组表示前缀1出现的次数。对于每个回答有两种情况:

    1. s[lr]s[lsim r]有偶数个1,则2sum[r]sum[l1]2|sum[r]-sum[l-1],sum[l1],sum[r]sum[l-1],sum[r]奇偶性相同。
    2. s[lr]s[lsim r]有奇数个1,则2sum[r]sum[l1]+12|sum[r]-sum[l-1]+1,sum[l1],sum[r]sum[l-1],sum[r]奇偶性不同。

    因为奇偶性关系具有如下传递性:

    1. xyx、y奇偶性相同,yzy、z奇偶性相同,则xzx、z奇偶性相同。
    2. xyx、y奇偶性相同,yzy、z奇偶性不同,则xzx、z奇偶性不同。
    3. xyx、y奇偶性不同,yzy、z奇偶性不同,则xzx、z奇偶性相同。

    方法1:

    这样我们就可以边带权——用1表示两个变量不同,用0表示两个变量相同。
    定义d[x]d[x]表示x与并查集中父亲的奇偶性关系,就可以很轻松地跑了。

    连接两个集合时,需要注意的是边权的长度。
    ansans表示两个变量的奇偶性相同(1表示不同,0表示相同),x,yx,y表示l1,rl-1,r的离散值,p,qp,qx,yx,y的祖先(并查集代表)。
    ans=dis[xp]xordis[pq]xordis[qy](disd)ans=dis[xsim p] operatorname{xor}dis[psim q]operatorname{xor} dis[qsim y](这里的dis是不同与d的)
    所以,若把pqp连向q,则d[p]=d[x]xord[y]xoransd[p]=d[x]operatorname{xor} d[y]operatorname{xor} ans.

    代码:

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<algorithm>
    #define g getchar()
    using namespace std;
    typedef long long ll;
    const int N=20010;
    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,d[N],fa[N];
    int findfa(int x) {
    	if(fa[x]==x)return x;
    	int root=findfa(fa[x]);
    	d[x]^=d[fa[x]];
    	return fa[x]=root;
    }
    struct rec {int l,r;bool ans;}q[N>>1];
    int a[N],tot;
    void disc() {
    	qr(n);qr(m);tot=0;
    	for(int i=1;i<=m;i++) {
    		qr(q[i].l),qr(q[i].r);
    		char s[7];scanf("%s",s);
    		q[i].ans=(s[0]=='o');
    		a[++tot]=q[i].l-1;a[++tot]=q[i].r;
    	}
    	sort(a+1,a+tot+1);
    	tot=unique(a+1,a+tot+1)-(a+1);
    }
    int main() {
    	disc();for(int i=1;i<=tot;i++)fa[i]=i;
    	for(int i=1;i<=m;i++) {
    		int x=lower_bound(a+1,a+tot+1,q[i].l-1)-a,
    			y=lower_bound(a+1,a+tot+1,q[i].r)-a,
    			ans=q[i].ans,tx,ty;
    		tx=findfa(x);ty=findfa(y);
    		if( tx == ty ) {
    			if( (d[x]^d[y]) != q[i].ans )
    				{write(i-1);puts("");return 0;}
    		}
    		else {
    			fa[ty]=tx;
    			d[ty]=d[x]^d[y]^ans;
    		}
    	}
    	write(m);puts("");
    	return 0;
    }
    
    

    总结:

    边带权并查集其实就是用并查集记录无向图的边。


    方法2:

    我们把每个变量(sum[x])(sum[x])拆成两个点——x_odd,x_even(分别表示sum[x]sum[x]为奇数的情况和sum[x]sum[x]为偶数的情况.)
    本质上是把点分成两个集合。
    x,yx,yl1,rl-1,r的离散值.那么对于每个回答,分两种情况讨论:

    1. 奇偶性相同。若x_odd与y_even在同一个集合内,则矛盾。
      否则,连接(x_odd,y_odd),(x_even,y_even).

    2. 奇偶性不同。若x_odd与y_odd在同一个集合内,则矛盾。
      否则,连接(x_odd,y_even),(x_even,y_odd).

    并查集维护的实际上是这样对称的关系。
    在这里插入图片描述
    这是一种更加直观的做法。

    代码:

    //扩展域——其实就是把一个点拆分成几个点。 
    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<algorithm>
    #define g getchar()
    using namespace std;
    typedef long long ll;
    const int N=20010;
    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],m;
    int findfa(int x) { return fa[x] == x?x:fa[x] = findfa(fa[x]);}
    struct node {int x,y;bool ans;}q[N>>1];
    int a[N],tot;
    void disc() {
    	qr(m);qr(m);tot=0;char s[8];
    	for(int i=1;i<=m;i++) {
    		qr(q[i].x);qr(q[i].y);
    		scanf("%s",s);
    		q[i].ans = (s[0] == 'o');
    		a[++tot]=q[i].x-1;
    		a[++tot]=q[i].y;
    	}
    	sort(a+1,a+tot+1);
    	tot=unique(a+1,a+tot+1)-(a+1);
    }
    int main() {
    	disc();for(int i=1;i<=2*tot;i++)fa[i]=i;
    	for(int i=1;i<=m;i++) {
    		int x=lower_bound(a+1,a+tot+1,q[i].x-1)-a;
    		int y=lower_bound(a+1,a+tot+1,q[i].y  )-a;
    		int x_odd = x,x_even = x + tot;
    		int y_odd = y,y_even = y + tot;
    		if(!q[i].ans) {
    			if(findfa(x_odd)==findfa(y_even)) {
    				write(i-1);puts("");
    				return 0;
    			}
    			fa[findfa(x_odd)]=findfa(y_odd);
    			fa[findfa(x_even)]=findfa(y_even);
    		}
    		else {
    			if(findfa(x_odd)==findfa(y_odd)) {
    				write(i-1);puts("");
    				return 0;
    			}
    			fa[findfa(x_odd)]=findfa(y_even);
    			fa[findfa(y_odd)]=findfa(x_even);
    		}
    	}
    	write(m);puts("");
    	return 0;
    }
    	
    
  • 相关阅读:
    详解threading模块:Condition类的使用
    concurrent.futures 模块使用说明
    细读Spring源码(四)什么是IOC?
    OAuth 2.0系列(六) OAuth令牌
    OAuth 2.0系列(七) OAuth 2.0可能存在的安全漏洞及预防方法
    细读Spring源码(一)refresh()方法概览
    HashMap源码解读() put方法的实现过程
    跟着字节码追踪JAVA中的基本运算
    细读Spring源码(六)Spring源码设计架构
    细读Spring源码(五)AOP从实战到源码
  • 原文地址:https://www.cnblogs.com/zsyzlzy/p/12373883.html
Copyright © 2011-2022 走看看