Yahoo Programming Contest 2019.E.Odd Subrectangles(思路 线性基)

本文探讨了一个关于矩阵操作的问题,目标是在给定的01矩阵中选取特定的行和列,使得交点处的元素和为奇数。通过使用线性代数中的线性基概念,文章提供了一种高效算法,用于计算所有可能方案的数量,适用于n,m≤300的情况。

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

题目链接


\(Description\)

给定一个\(n\times m\)\(01\)矩阵。求任意选出\(r\)行、\(c\)列(共\(2^{n+m}\)种方案),使得这\(r\)\(c\)列的交的位置的\(r\times c\)个数的和为奇数,的方案数有多少。
(...我也不知道怎么就表述成这样了,反正挺好理解)
\(n,m\leq300\)

\(Solution\)

假设已经确定了选择某些行,然后把每一行\(m\)个数看做一个\(m\)位二进制数。
如果这些行异或和为\(0\),那怎么选列也不行。
否则每一列异或这些行是\(0\)还是\(1\)是确定的。假设有\(a\)列异或后为\(1\)\(b\)列异或后为\(0\)\(a+b=m\)。我们要从\(a\)中选出奇数个,从\(b\)中随便选,那么方案数是\(2^{a-1}\cdot2^b=2^{m-1}\)(从\(n\)中选出奇数个数的方案数。。\(C_n^1+C_n^3+C_n^5+...=\frac{2^n}{2}=2^{n-1}\))。
也就是不管行怎么选,只要异或和不为\(0\),列就有\(2^{m-1}\)种方案。
异或和不为\(0\)的方案数=\(2^n-\)异或和为\(0\)的方案数。异或和为\(0\)的方案数可以用线性基求,是\(2^{n-r}\)\(n\)是元素总数,\(r\)是矩阵的秩,也就是线性基中的元素个数),所以答案就是\((2^n-2^{n-r})\cdot2^{m-1}\)
这里线性基插入还是一样的,把一行看做\(m\)位数就好了。

复杂度\(O(n^3)\)\(O(\frac{n^3}{w})\)


//7ms   896KB
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define mod 998244353
typedef long long LL;
const int N=305;

int A[N][N],B[N][N];

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-48,c=gc());
    return now;
}
inline int FP(int x,int k)
{
    int t=1;
    for(; k; k>>=1,x=1ll*x*x%mod)
        if(k&1) t=1ll*t*x%mod;
    return t;
}

int main()
{
    int n=read(),m=read();
    for(int i=1; i<=n; ++i)
        for(int j=1; j<=m; ++j) A[i][j]=read();
    int r=0;
    for(int i=1; i<=n; ++i)
        for(int j=1; j<=m; ++j)
            if(A[i][j])//x>>j&1
            {
                if(B[j][j]) for(int k=j; k<=m; ++k) A[i][k]^=B[j][k];
                else
                {
                    for(int k=j; k<=m; ++k) B[j][k]=A[i][k];
                    ++r; break;
                }
            }
    printf("%d\n",(FP(2,n+m-1)+mod-FP(2,n-r+m-1))%mod);

    return 0;
}

bitset优化:

//5ms   256KB
#include <cstdio>
#include <cctype>
#include <bitset>
#include <algorithm>
#define gc() getchar()
#define mod 998244353
typedef long long LL;
const int N=305;

std::bitset<N> A[N],B[N];

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-48,c=gc());
    return now;
}
inline int FP(int x,int k)
{
    int t=1;
    for(; k; k>>=1,x=1ll*x*x%mod)
        if(k&1) t=1ll*t*x%mod;
    return t;
}

int main()
{
    int n=read(),m=read();
    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])//x>>j&1
            {
                if(B[j][j]) A[i]^=B[j];
                else
                {
                    B[j]=A[i];
                    ++r; break;
                }
            }
    printf("%d\n",(FP(2,n+m-1)+mod-FP(2,n-r+m-1))%mod);

    return 0;
}

转载于:https://www.cnblogs.com/SovietPower/p/10389645.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值