牛客国庆集训派对Day2 A 矩阵乘法

版权声明:本文为博主原创文章,转载请著名出处 http://blog.csdn.net/u013534123 https://blog.csdn.net/u013534123/article/details/82928651

就是让你做一个矩阵乘法,但是可能暴力做复杂度比较高(不会告诉你暴力也能过)。

题目已经大致告诉你优化的方法了。如果两个二进制向量点乘,那么可以用与运算代替乘法运算,最后统计结果中1的个数。而本题就是让你实现这个过程。

可以看到,第二个矩阵是一个01矩阵,所以说可以用类似的方法。第一个矩阵是一个16进制的矩阵,但是我们可以把这个16进制的2^16级别的数字拆成16个矩阵。而第二个矩阵我们也可以看作16个相同的矩阵。这里第0~15个矩阵分别对应第0~15位,也即2的对应次方。如此拆分之后,就变成了16个01矩阵相乘,并且不用考虑进位。

由于是01矩阵,根据矩阵乘法的定义,结果矩阵的某一个位置(i,j),相当于第一个矩阵的第i行和第二个矩阵的第j列相乘。这也就是两个01向量的点乘,恰好本题的p的大小为64,所有每一个向量可以用一个unsigned long long来存下。于是相乘就可以转化为两个向量对应的数字的按位与,最后看结果有多少个1,再乘以对应2的幂,就是这一位对最后答案的贡献。这样计算最多16次即可,复杂度是O(16*NM)。具体见代码:

#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned LL
using namespace std;
 
ULL a[16][4096],b[16][4096];
int n,m,p;
 
int main()
{
    char s[100];
    scanf("%d%d%d",&n,&p,&m);
    for(int i=0;i<n;i++)
        for(int j=0;j<p;j++)
        {
            int x;
            scanf("%x",&x);
            for(int k=0;k<=15;k++)
                if (x&(1<<k)) a[k][i]|=(1ULL<<j);
        }
    for(int i=0;i<m;i++)
    {
        ULL x=0;
        scanf("%s",s);
        for(int j=0;j<p;j++)
            if (s[j]=='1') x|=(1ULL<<j);
        for(int j=0;j<16;j++) b[j][i]=x;
    }
    int ans=0;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        {
            int res=0;
            for(int k=0;k<16;k++)
                res+=(1<<k)*__builtin_popcountll(a[k][i]&b[k][j]);
            ans^=res;
        }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/82928651