BZOJ-3517 翻硬币(异或方程组)
作者:互联网
题目描述
有一个 \(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\) 行第 \(b\) 列的硬币的状态由以下方程决定:
\[\left( \bigoplus_{j=1}^n x_{a,j} \right)\oplus \left (\bigoplus_{i=1}^n x_{i,b}\right)\oplus x_{a,b}\oplus A_{a,b}=0 \]根据异或的性质:\(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;
}
标签:right,硬币,int,3517,异或,bigoplus,oplus,left,BZOJ 来源: https://www.cnblogs.com/DestinHistoire/p/14070851.html