前言

前几天看了一下 201720172017 年论文《非常规大小分块算法初探》,里面有一节讲到了 010101 矩阵乘法,介绍了一种 O(n3ωlog⁡n)\mathcal O({n^3\over\omega\sqrt{\log n}})O(ωlognn3) 的分块做法。然而其代码实现难度高,复杂度优化效果小,我曾向 Rainybunny\sf RainybunnyRainybunny 说,不会真的有人写这玩意儿骗分吧!

结果我还是写了分块 010101 矩阵乘法来骗分。但是并不是上面的方法。是我无意中想到的一个分块方法。为什么要写出来呢?可能也是为了留作纪念吧……

概述

这里的 010101 矩阵是,在模 222 意义下(在域 GF(2)\Bbb{GF}(2)GF(2) 内)进行所有操作,包括答案矩阵也要对 222 取模。

二维分块

这是论文中的方法,先简单讲讲。不妨设要计算 A×BA\times BA×B,将两个矩形都划分成若干 S×SS\times SS×S 大小的子矩形。花费 O(4S2S3ω)\mathcal O(4^{S^2}{S^3\over \omega})O(4S2ωS3) 的时间,预处理每一种不同的情况对应的结果。然后用 O[(nS)3S2ω]=O(n3Sω)\mathcal O[({n\over S})^3{S^2\over\omega}]=\mathcal O({n^3\over S\omega})O[(Sn)3ωS2]=O(Sωn3) 的复杂度合并结果。取 S=log⁡4n2S=\sqrt{{\log_4 n}\over 2}S=2log4n 时,有最优复杂度 O(n3ωlog⁡n)\mathcal O({n^3\over{\omega \sqrt{\log n}}})O(ωlognn3)

一维分块

为什么上面的方法不优呢?因为 S⩽ωS\leqslant\omegaSω,所以 bitset\tt bitsetbitset 的效用发挥不出来了。bitset\tt bitsetbitset 才是最强的分块者!

考虑 ω\omegaωSSS 的分块效果同时存在。回到原式 Ci,j=∑kAi,kBk,jC_{i,j}=\sum_k A_{i,k}B_{k,j}Ci,j=kAi,kBk,j 上,不妨设 iii 是未分块的一维。能不能对 jjjSSS 分块呢?相当于矩阵 AAA 的某个行向量,要点乘 SSS 个列向量。似乎并没有冗余计算。估计行不通。

所以考虑对 kkkSSS 分块。也就是 AAA 的行向量的一部分,长度仅为 SSS,代表 BBB 的某些行向量是否被选择,然后异或到第 iii 行上。长度仅为 SSS,这提示我们 Method of four Russians\text{Method of four Russians}Method of four Russians,即讨论 2S2^S2S 种不同的 Ai,k  (k∈[l,r])A_{i,k}\;(k\in[l,r])Ai,k(k[l,r]) 的情况,并直接计算出对第 iii 行的贡献。

那么 BBB 中每 SSS 行就要暴力计算一次,这是 O(nS⋅2S⋅nω)\mathcal O({n\over S}\cdot 2^S\cdot{n\over\omega})O(Sn2Sωn) 的,而 AAA 中只需要 O(n2S)\mathcal O({n^2\over S})O(Sn2) 次行向量操作,是 O(n3ωS)\mathcal O({n^3\over \omega S})O(ωSn3) 的,取 S=log⁡nS=\log nS=logn 有最优复杂度 O(n3ωlog⁡n)\mathcal O({n^3\over \omega\log n})O(ωlognn3),且代码实现极其简单。

代码实现

nnn 是矩阵大小,stepstepstep 是块的大小,已在主函数内求出。

const int MAXN = 2000;
const int LOGN = 10;
bitset<MAXN> dp[1<<LOGN];
int lowbit[1<<LOGN], n, step;
struct Matrix{
	bitset<MAXN> a[MAXN];
	inline void clear(){
		for(int i=0; i!=n; ++i)
			a[i].reset();
	}
	Matrix operator * (const Matrix &b) const {
		Matrix c; c.clear();
		for(int l=0,r=step; l!=n; l=r,r+=step){
			if(__builtin_expect(r > n, false))
				r = n; // not to exceed n
			for(int S=1; S!=(1<<step); ++S){
				const int &id = lowbit[S];
				dp[S] = dp[S^(1<<id)]^b.a[l+id];
			}
			for(int i=0,now=0; i!=n; ++i,now=0){
				for(int j=l; j!=r; ++j)
					now ^= (a[i].test(j))<<(j-l);
				c.a[i] ^= dp[now]; // find in table
			}
		}
		return c;
	}
};
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐