1. 程式人生 > 實用技巧 >P3631 [APIO2011]方格染色 帶權並查集

P3631 [APIO2011]方格染色 帶權並查集

題意:

給定大小為\(n*m\)的矩陣,對矩陣進行染色,顏色只有兩種,要求矩陣內任意大小為\(2*2\)的子矩陣顏色只能為1種顏色3個,另1種顏色1個,現在固定k個格子的顏色,求有多少種染色方案

範圍&性質:\(1\le n,m,k\le 10^5\)

分析:

不看題解,我可能都想不出來,這是一道並查集

我們可以利用位運算對題目進行簡化,對於每一個2*2的矩陣,按題目要求異或得到的值為1,即存在$ a(i,j); xor ;a(i+1,j); xor ; a(i,j+1); xor ;a(i+1,j+1)=1\(,遞推可以得到,第一行和第一列確定後,整張圖的顏色也隨之確定。反之,如果\)

(x,y)\(的值確定,對\)(1,1)$也會有所限制

分情況討論:

  1. \((x,y)\)都為偶數時,以\((1,1),(x,y)\)為頂點的四邊形內共有奇數個格子,異或和為1,且除了\((1,1),(x,1),(1,y),(x,y)\)以外的格子都異或了偶數次,可以得到

    \[a(1,1)\; xor \;a(x,1)\; xor \; a(1,y)\; xor \;a(x,y)=1 \]

    \[a(1,1)\; xor \;a(x,1)\; xor \; a(1,y) =1\;xor \;a(x,y) \]

  2. \((x,y)\)至少一個為奇數時,情況剛好相反,留給讀者思考

所以\(a(x,y)\)

\(a(1,1)\)有三種關係,相同,相反,無關,前兩種關係都可以看做聯通,最後只需要統計\(a(1,1)=0 \ | \ 1\)時,全圖有多少個連通塊,答案即為\(2^{連通塊個數-1}\),因為\(a(1,1)\)固定

tips:

  1. 對於\(a(1,1)=1\)的情況只需要將所有固定的塊顏色翻轉,然後當做\(a(1,1)=0\)處理就好了

  2. 對於\(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;
    }