【2018 BSUIR Final C】Partial Sums 题解

题目大意

  给定一个 n × m n \times m n×m 的 01 矩阵 A 0 A_0 A0。定义一次操作为将这个矩形每个元素求异或前缀和,即 A k [ i , j ] = ( ∑ u = 1 i ∑ v = 1 j A k − 1 [ u , v ] )   m o d   2 A_k[i,j]=(\sum_{u=1}^i\sum_{v=1}^j A_{k-1}[u,v]) \bmod 2 Ak[i,j]=(u=1iv=1jAk1[u,v])mod2
  求一个最小的正整数 k k k,使得 A 0 = A k A_0=A_k A0=Ak

   n × m ≤ 1 0 6 n\times m \leq 10^6 n×m106

\\
\\
\\

题解

  首先,可以发现答案一定是 2 2 2 的幂。
  可以这么说明这个结论,设 k i , j k_{i,j} ki,j 表示左上角为 ( 1 , 1 ) (1,1) (1,1) 右下角为 ( i , j ) (i,j) (i,j) 的子矩形的答案,那么 k i , j k_{i,j} ki,j 至少为 l c m ( k i − 1 , j , k i , j − 1 ) lcm(k_{i-1,j},k_{i,j-1}) lcm(ki1,j,ki,j1),如果这 l c m lcm lcm 轮过后 ( i , j ) (i,j) (i,j) 的元素没变,那么 k i , j k_{i,j} ki,j 就等于这个,否则还要再来 l c m lcm lcm 轮把它变回来,即 k i , j = l c m ⋅ 2 k_{i,j}=lcm \cdot 2 ki,j=lcm2。而又因为 k 1 , 1 = 1 k_{1,1}=1 k1,1=1,因此可以归纳证明任意 k i , j k_{i,j} ki,j 一定是 2 2 2 的幂。

  然后想一个问题,如果经过 k k k 轮操作,那么一个格子 ( x , y ) (x,y) (x,y) 对其右下的一个格子 ( x + Δ x , y + Δ y ) (x+\Delta x,y+\Delta y) (x+Δx,y+Δy) 的贡献是多少?
  这等价于一个棋子从 ( x , y ) (x,y) (x,y) 开始,每次跳到其右下方的一个位置(包括自己本身),跳 k k k 步跳到 ( x + Δ x , y + Δ y ) (x+\Delta x,y+\Delta y) (x+Δx,y+Δy),的方案数是多少。
  这显然是 ( Δ x + k − 1 k − 1 ) ⋅ ( Δ y + k − 1 k − 1 ) \binom{\Delta x+k-1}{k-1} \cdot \binom{\Delta y+k-1}{k-1} (k1Δx+k1)(k1Δy+k1)
  而根据 Lucas 定理,当 k k k 2 2 2 的幂时,只有当 Δ x \Delta x Δx Δ y \Delta y Δy 都是 k k k 的倍数的时候,这个式子才   m o d     2 = 1 \bmod~2=1 mod 2=1

  于是我们就可以 k = 1 , 2 , 4 , 8 , ⋯ k=1,2,4,8,\cdots k=1,2,4,8, 这样枚举答案,每次枚举中,每个位置只考虑 Δ x \Delta x Δx Δ y \Delta y Δy k k k 倍数的位置的贡献,可以 O ( n m ) O(nm) O(nm) 得出最终矩阵并判断。(看代码)
  同时这样也说明答案不会很大,最多判断 log ⁡ n m \log nm lognm 次。

代码

#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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值