归纳所猜半结论推出完整结论:CF1592F1

本文搬运自本人高中时期CSDN博客,若图片加载不出来,可到原文查看:https://blog.csdn.net/zhangtingxiqwq/article/details/133757947

https://www.luogu.com.cn/problem/CF1592F1

场上猜了个结论,感觉只会操作1。然后被样例1hack了。然后就猜如果 (n,m)(n,m) 为1则翻转4操作,被#14hack了。然后就猜4操作只会进行一次,然后就不知道怎么做下去了。


上面猜的结论都正确,但是既然猜结论了,为什么不考虑先证明一波?

考虑2次操作4,代价为6,只有两种情况:

在这里插入图片描述

而他们都可以用操作1表示出来。

然后考虑怎么做。其实感觉没有操作4时,每个位置是否翻转都可以直接O(1)算出来。但这存在一定难度。

我当时写的是:

在这里插入图片描述

这样子存在逻辑联系,不方便直接表示,所以应该考虑把if取得。

怎么去?就多列几个表示出来。(相当于多一个媒介)

vi,j=ai,jsi1,jsi,j1si1,j1pi,j=vi,jsi1,jsi,j1si1,j1v_{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}

然后我们发现了 ssaa 相同。

然后发现翻转4只会改变4个位置。

然后操作4有贡献只当这4个位置同时改变。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#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;
}