https://www.luogu.com.cn/problem/CF1592F1
场上猜了个结论,感觉只会操作1。然后被样例1hack了。然后就猜如果 ( n , m ) (n,m) (n,m) 为1则翻转4操作,被#14hack了。然后就猜4操作只会进行一次,然后就不知道怎么做下去了。
上面猜的结论都正确,但是既然猜结论了,为什么不考虑先证明一波?
考虑2次操作4,代价为6,只有两种情况:
而他们都可以用操作1表示出来。
然后考虑怎么做。其实感觉没有操作4时,每个位置是否翻转都可以直接O(1)算出来。但这存在一定难度。
我当时写的是:
这样子存在逻辑联系,不方便直接表示,所以应该考虑把if取得。
怎么去?就多列几个表示出来。(相当于多一个媒介)
v i , j = a i , j ⊗ s i − 1 , j ⊗ s i , j − 1 ⊗ s i − 1 , j − 1 p i , j = v i , j ⊗ s i − 1 , j ⊗ s i , j − 1 ⊗ s i − 1 , j − 1 v_{i,j}=a_{i,j}\otimes s_{i-1,j}\otimes s_{i,j-1}\otimes s_{i-1,j-1}\\p_{i,j} = v_{i,j}\otimes s_{i-1,j}\otimes s_{i,j-1}\otimes s_{i-1,j-1} vi,j=ai,j⊗si−1,j⊗si,j−1⊗si−1,j−1pi,j=vi,j⊗si−1,j⊗si,j−1⊗si−1,j−1
然后我们发现了 s s s 和 a a a 相同。
然后发现翻转4只会改变4个位置。
然后操作4有贡献只当这4个位置同时改变。
cpp
#include<bits/stdc++.h>
using namespace std;
//#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
//#define M
//#define mo
#define N 510
int n, m, i, j, k, T;
int a[N][N], p[N][N], ans;
char str[N];
signed main()
{
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// srand(time(NULL));
// T=read();
// while(T--) {
//
// }
auto calc = [&] (int x, int y) -> int {
return a[x][y]^a[x+1][y]^a[x][y+1]^a[x+1][y+1];
};
n=read(); m=read();
for(i=1; i<=n; ++i) {
scanf("%s", str+1);
for(j=1; j<=m; ++j)
if(str[j]=='B') a[i][j]=1;
}
for(i=n; i>=1; --i)
for(j=m; j>=1; --j) {
p[i][j]=(p[i+1][j]^p[i][j+1]^p[i+1][j+1]);
if(a[i][j]^p[i][j]) p[i][j]^=1, ++ans;
ans+=calc(i, j);
if(i!=n && j!=m && calc(i, j) && calc(i, m) && calc(n, j) && calc(n, m)) k=-1;
}
printf("%d", ans+k);
return 0;
}