[Atcoder Yahoo Contest 2019]E.Odd Subrectangles(思维+线性基)

本文介绍了一种巧妙的方法来解决01矩阵中子集异或和为1的问题,通过线性基算法统计子集异或和为0的个数,并结合组合数学原理,最终求得总方案数。时间复杂度为O(n^3/w),使用bitset进行优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Score : 800 800 800 points

题面

传送门
翻译有时间再补…

题解

很巧妙的一道题。
在此特别感谢一下SovietPower的博客文章

首先这是一个01矩阵,因此可以将和的奇偶性看成是异或和是否为1。
按照题解的套路,假如我们已经选好了一些列。
这样的话我们可以确定每行的异或和是 0 0 0还是 1 1 1
设有 a a a行异或和为 1 1 1 b b b行异或和为 0 0 0,显然有 a + b = m a+b=m a+b=m
题目要求总异或和为 1 1 1,可以在 a a a行中选奇数行, b b b列中随便选。
a a a行中选奇数行的方案数是: C a 1 + C a 3 + . . . = 2 a − 1 C^{1}_{a}+C^{3}_{a}+...=2^{a-1} Ca1+Ca3+...=2a1(可以查一下杨辉三角的性质)
因此确定一些列的方案数是 2 a − 1 ∗ 2 b = 2 m − 1 2^{a-1}*2^b=2^{m-1} 2a12b=2m1.
但是如果 a = 0 a=0 a=0时,是不满足条件的。
考虑统计这些不合法的情况。
可以转换为将每一行都看成一个二进制数列,统计一下其中子集异或和为0的个数。
这是可以用线性基解决的经典问题。
根据线性基的结论,在线性基里选取任意子集的异或和都不为0。
设线性基的大小为 r r r,那么异或等于零的个数就是 2 n − r 2^{n-r} 2nr
那么总答案就是 ( 2 n − 2 n − r ) ∗ 2 m − 1 (2^n-2^{n-r})*2^{m-1} (2n2nr)2m1
二进制数列可以用bitset优化一下。
因此时间复杂度为 O ( n 3 w ) O(\frac{n^3}{w}) O(wn3)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<bitset>
using namespace std;
#define MAXN 300
#define MOD 998244353
int n,m;
bitset<MAXN+5> A[MAXN+5],B[MAXN+5];
int read()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f; 
}
int fst_pow(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)res=(1LL*res*a)%MOD;
		a=(1LL*a*a)%MOD;
		b>>=1;
	}
	return res;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		A[i][j]=(read()==1);
	int r=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		if(A[i][j])
		{
			if(B[j][j])A[i]^=B[j];
			else
			{B[j]=A[i];r++;break;}
		}
	printf("%d",(fst_pow(2,n+m-1)-fst_pow(2,n-r+m-1)+MOD)%MOD);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值