P3631 [APIO2011]方格染色 帶權並查集
阿新 • • 發佈:2020-09-14
題意:
給定大小為\(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),(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) \]
-
當\((x,y)\)至少一個為奇數時,情況剛好相反,
留給讀者思考
所以\(a(x,y)\)
tips:
-
對於\(a(1,1)=1\)的情況只需要將所有固定的塊顏色翻轉,然後當做\(a(1,1)=0\)處理就好了
-
對於\(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; }