BZOJ-3517 翻硬幣(異或方程組)
阿新 • • 發佈:2020-12-01
題目描述
有一個 \(n\) 行 \(n(n\leq 1000)\) 列的棋盤,每個格子上都有一個硬幣,且 \(n\) 為偶數。每個硬幣要麼是正面朝上,要麼是反面朝上。每次操作你可以選定一個格子 \((x,y)\),然後將第 \(x\) 行和第 \(y\) 列的所有硬幣都翻面。求將所有硬幣都變成同一個面最少需要的運算元。
分析
一個硬幣被翻兩次和不翻是等價的,因此每個硬幣至多被翻一次,設 \(x_{ij}\) 為第 \(i\) 行第 \(j\) 列的硬幣是否被翻,\(A_{i,j}\) 為硬幣初始狀態,\(A_{i,j}=1\) 為正面朝上,\(A_{i,j}=0\) 為反面朝上。\(n\) 為偶數這個條件提示我們從異或角度考慮,則第 \(a\)
根據異或的性質:\(a\oplus b=c\Longrightarrow c\oplus b=a\),把 \(A_{a,b}\) 移動到等號右邊,即:
\[\left( \bigoplus_{j=1}^n x_{a,j} \right)\oplus \left (\bigoplus_{i=1}^n x_{i,b}\right)\oplus x_{a,b}=A_{a,b} \]列出 \(n^2\) 個方程,有重複項的方程相互異或抵消掉,最後得到:
\[x_{a,b}=\left( \bigoplus_{j=1}^n A_{a,j} \right)\oplus \left (\bigoplus_{i=1}^n A_{i,b}\right)\oplus A_{a,b} \]預處理一下每行每列異或值的字首和,求 $n^2 $ 個數時間複雜度可以降到 \(O(n^2)\)。
程式碼
#include<bits/stdc++.h> using namespace std; const int N=1010; int A[N][N]; int sum1[N],sum2[N]; int main() { int n; cin>>n; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%1d",&A[i][j]); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) sum1[i]=sum1[i]^A[i][j]; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) sum2[j]=sum2[j]^A[i][j]; int ans=0; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { int temp=sum1[i]^sum2[j]; temp=temp^A[i][j]; ans=ans+temp; } } cout<<min(ans,n*n-ans)<<endl; return 0; }