题目大意
给定一个 n×m 的 01 矩阵 A0。定义一次操作为将这个矩形每个元素求异或前缀和,即 Ak[i,j]=(∑u=1i∑v=1jAk−1[u,v])mod2。
求一个最小的正整数 k,使得 A0=Ak。
n×m≤106
题解
首先,可以发现答案一定是 2 的幂。
可以这么说明这个结论,设 ki,j 表示左上角为 (1,1) 右下角为 (i,j) 的子矩形的答案,那么 ki,j 至少为 lcm(ki−1,j,ki,j−1),如果这 lcm 轮过后 (i,j) 的元素没变,那么 ki,j 就等于这个,否则还要再来 lcm 轮把它变回来,即 ki,j=lcm⋅2。而又因为 k1,1=1,因此可以归纳证明任意 ki,j 一定是 2 的幂。
然后想一个问题,如果经过 k 轮操作,那么一个格子 (x,y) 对其右下的一个格子 (x+Δx,y+Δy) 的贡献是多少?
这等价于一个棋子从 (x,y) 开始,每次跳到其右下方的一个位置(包括自己本身),跳 k 步跳到 (x+Δx,y+Δy),的方案数是多少。
这显然是 (k−1Δx+k−1)⋅(k−1Δy+k−1)。
而根据 Lucas 定理,当 k 为 2 的幂时,只有当 Δx 和 Δy 都是 k 的倍数的时候,这个式子才 mod 2=1。
于是我们就可以 k=1,2,4,8,⋯ 这样枚举答案,每次枚举中,每个位置只考虑 Δx 和 Δy 是 k 倍数的位置的贡献,可以 O(nm) 得出最终矩阵并判断。(看代码)
同时这样也说明答案不会很大,最多判断 lognm 次。
代码
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> #define fo(i,a,b) for(int i=a;i<=b;i++) using namespace std;
typedef long long LL;
const int maxn=1e6+5;
int n,m; bool a[maxn];
inline int id(int i,int j) {return (i-1)*m+j;}
void ReadBit(bool &data) { char ch=getchar(); while (ch!='0' && ch!='1') ch=getchar(); data=(ch=='1'); }
bool b[maxn]; bool check(int k) { fo(i,1,n) fo(j,1,m) { b[id(i,j)]=a[id(i,j)]; if (i>k) b[id(i,j)]^=b[id(i-k,j)]; if (j>k) b[id(i,j)]^=b[id(i,j-k)]; if (i>k && j>k) b[id(i,j)]^=b[id(i-k,j-k)]; if (b[id(i,j)]!=a[id(i,j)]) return 0; } return 1; }
int main() { scanf("%d %d",&n,&m); fo(i,1,n) fo(j,1,m) ReadBit(a[id(i,j)]); int k=1; while (!check(k)) k<<=1; printf("%d\n",k); }
|