P3631 [APIO2011]方格染色 带权并查集

题意:

给定大小为 n ∗ m n*m nm的矩阵,对矩阵进行染色,颜色只有两种,要求矩阵内任意大小为 2 ∗ 2 2*2 22的子矩阵颜色只能为1种颜色3个,另1种颜色1个,现在固定k个格子的颜色,求有多少种染色方案

范围&性质: 1 ≤ n , m , k ≤ 1 0 5 1\le n,m,k\le 10^5 1n,m,k105

分析:

不看题解,我可能都想不出来,这是一道并查集

我们可以利用位运算对题目进行简化,对于每一个2*2的矩阵,按题目要求异或得到的值为1,即存在 a ( i , j )    x o r    a ( i + 1 , j )    x o r    a ( i , j + 1 )    x o r    a ( i + 1 , j + 1 ) = 1 a(i,j)\; xor \;a(i+1,j)\; xor \; a(i,j+1)\; xor \;a(i+1,j+1)=1 a(i,j)xora(i+1,j)xora(i,j+1)xora(i+1,j+1)=1,递推可以得到,第一行和第一列确定后,整张图的颜色也随之确定。反之,如果 ( x , y ) (x,y) (x,y)的值确定,对 ( 1 , 1 ) (1,1) (1,1)也会有所限制

分情况讨论:

  1. ( x , y ) (x,y) (x,y)都为偶数时,以 ( 1 , 1 ) , ( x , y ) (1,1),(x,y) (1,1),(x,y)为顶点的四边形内共有奇数个格子,异或和为1,且除了 ( 1 , 1 ) , ( x , 1 ) , ( 1 , y ) , ( x , y ) (1,1),(x,1),(1,y),(x,y) (1,1),(x,1),(1,y),(x,y)以外的格子都异或了偶数次,可以得到

    a ( 1 , 1 )    x o r    a ( x , 1 )    x o r    a ( 1 , y )    x o r    a ( x , y ) = 1 a(1,1)\; xor \;a(x,1)\; xor \; a(1,y)\; xor \;a(x,y)=1 a(1,1)xora(x,1)xora(1,y)xora(x,y)=1

    a ( 1 , 1 )    x o r    a ( x , 1 )    x o r    a ( 1 , y ) = 1    x o r    a ( x , y ) a(1,1)\; xor \;a(x,1)\; xor \; a(1,y) =1\;xor \;a(x,y) a(1,1)xora(x,1)xora(1,y)=1xora(x,y)

  2. ( x , y ) (x,y) (x,y)至少一个为奇数时,情况刚好相反,留给读者思考

所以 a ( x , y ) a(x,y) a(x,y) a ( 1 , 1 ) a(1,1) a(1,1)有三种关系,相同,相反,无关,前两种关系都可以看做联通,最后只需要统计 a ( 1 , 1 ) = 0   ∣   1 a(1,1)=0 \ | \ 1 a(1,1)=0  1时,全图有多少个连通块,答案即为 2 连 通 块 个 数 − 1 2^{连通块个数-1} 21,因为 a ( 1 , 1 ) a(1,1) a(1,1)固定

tips:

  1. 对于 a ( 1 , 1 ) = 1 a(1,1)=1 a(1,1)=1的情况只需要将所有固定的块颜色翻转,然后当做 a ( 1 , 1 ) = 0 a(1,1)=0 a(1,1)=0处理就好了

  2. 对于 a ( x , y ) a(x,y) a(x,y)坐标均为偶数的情况将颜色翻转后当做第二种情况(至少一个为奇数)处理就好

    代码:

    #include<bits/stdc++.h>
    
    using namespace std;
    
    namespace zzc
    {
          
          
    	const int mod =1e9;
        const int maxn = 2e5+5;
        int n,m,k;
        int x[maxn],y[maxn],w[maxn],fa[maxn],g[maxn];
        
        int qpow(int x,int y)
        {
          
          
        	int res=1;
        	while(y)
        	{
          
          
        		if(y&1) res=(long long)res*x%mod;
        		x=(long long)x*x%mod;
        		y>>=1;
    		}
    		return res;
    	}
        
        int find(int x)
        {
          
          
        	if(x==fa[x]) return x;
        	int fx=find(fa[x]);
        	g[x]^=g[fa[x]];
        	return fa[x]=fx;
    	}
        
        int work(int opt)
        {
          
          
        	for(int i=1;i<=n+m;i++)
        	{
          
          
        		fa[i]=i;
        		g[i]=0;
    		}
    		fa[n+1]=1;
        	if(opt==1)
        	{
          
          
        		for(int i=1;i<=k;i++)
        		{
          
          
        			if(x[i]>1&&y[i]>1)
        			{
          
          
        				w[i]^=1;
    				}
    			}
    		}
    		for (int i=1;i<=k;i++)
    		{
          
          
    			int a=x[i],b=y[i],c=w[i];
    			if ( a!=1 || b!=1 )
    			{
          
          
    				int fx=find(a),fy=find(n+b);
    				int tmp=g[a]^g[n+b]^c;
    				if (fx!=fy)
    				{
          
          
    					fa[fy]=fx;
    					g[fy]=tmp;
    				}
    				else if (tmp) return 0;
    			}
    		}
    		int cnt=0;
    		for (int i=1;i<=n+m;i++)
    		{
          
          
    			if (i==find(i)) cnt++;
    		}
    		return qpow(2,cnt-1);
    	}
        
        void work()
        {
          
          
        	int flag=-1;
        	scanf("%d%d%d",&n,&m,&k);
        	for(int i=1;i<=k;i++)
        	{
          
          
        		scanf("%d%d%d",&x[i],&y[i],&w[i]);
        		if(x[i]==1&&y[i]==1)
        		{
          
          
        			flag=w[i];
    			}
    			else if(!(x[i]&1)&&!(y[i]&1))
    			{
          
          
    				w[i]^=1;
    			}
    		}
    		if(flag!=-1)
    		{
          
          
    			printf("%d",work(flag));
    			return ;
    		}
    		else
    		{
          
          
    			printf("%d",(work(1)+work(0))%mod);
    			return ;
    		}
    	}
    	 
    }
    
    int main()
    {
          
          
    	zzc::work();
    	return 0;
    }
    

猜你喜欢

转载自blog.csdn.net/youth518/article/details/108573753