1 条题解
-
0
自动搬运
来自洛谷,原作者为

Taoran_01
May.搬运于
2025-08-24 22:58:18,当前版本为作者最后更新于2024-07-30 06:40:56,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
知识前置
哈希
一种映射,从大值域到小值域。本文中的哈希均指字符串哈希,即将数列 视作 进制数后对 取模的结果,即 $H(a)=(\sum\limits_{i=1}^na_i\times p^{n-i})\operatorname{mod}M$。其中, 称为底, 称为模数。有以下性质:
- 当在数列 的末尾插入数 时,数列 的哈希值 。
- 已知数列 的哈希值为 ,数列 的哈希值为 ,而数列 由数列 和数列 拼接而成,则 。
上述两条性质不难理解,就是 进制数的位移运算与加减运算。
详见OI-WIki中的第一种定义,或参考李煜东所著的《算法竞赛进阶指南》第75页。
二维前缀和
二维哈希参考了二维前缀和的思想,使得哈希值可以 地从角落哈希值求出。
设 $S'_{x_1,y_1,x_2,y_2}=\sum\limits_{i=x_1}^{x_2}\sum\limits_{j=y_1}^{y_2}a_{i,j}$,$S_{x,y}=S'_{1,1,x,y}=\sum\limits_{i=1}^x\sum\limits_{j=1}^ya_{i,j}$,则有:
$$S_{x,y}=a_{x,y}+S_{x-1,y}+S_{x,y-1}-S_{x-1,y-1}\\ S'_{x_1,y_1,x_2,y_2}=S_{x_2,y_2}-S_{x_1-1,y_2}-S_{x_2,y_1-1}+S_{x_1-1,y_1-1} $$自己画个图证明下就会了,详见OI-Wiki。
二维哈希
将一个二维矩阵映射到一个值上,即:
$$A=\begin{bmatrix} a_{1,1}&\cdots&a_{1,m}\\ \vdots&\ddots&\vdots\\ a_{n,1}&\cdots&a_{n,m} \end{bmatrix}\longrightarrow H(A)\in[0,M)\cap\mathbb Z $$
整个矩阵哈希值的获取
步骤一 按行哈希
将矩阵 按行进行哈希得到矩阵 。
$$A=\begin{bmatrix} \color{orange}a_{1,1}&\color{orange}\cdots&\color{orange}a_{1,m}\\ \vdots&\ddots&\vdots\\ a_{n,1}&\cdots&a_{n,m} \end{bmatrix}\longrightarrow F=\begin{bmatrix} f_{1,1}&\cdots&\color{orange}f_{1,m}\\ \vdots&\ddots&\vdots\\ f_{n,1}&\cdots&f_{n,m} \end{bmatrix} $$
即,将矩阵 视作 个长度为 的一维数列。设底为 ,模数为 ,则转移公式如下:
$$f_{x,y}=(\sum_{i=1}^ya_{x,i}\times p_1^{y-i})\operatorname{mod}M $$根据一维哈希的性质,将公式进行优化:
$$f_{x,y}=(f_{x,y-1}\times p_1+a_{x,y})\operatorname{mod}M $$步骤二 按列哈希
将矩阵 按列进行哈希得到矩阵 。
$$F=\begin{bmatrix} \color{orange}f_{1,1}&\cdots&f_{1,m}\\ \color{orange}\vdots&\ddots&\vdots\\ \color{orange}f_{n,1}&\cdots&f_{n,m} \end{bmatrix}\longrightarrow G=\begin{bmatrix} g_{1,1}&\cdots&g_{1,m}\\ \vdots&\ddots&\vdots\\ \color{orange}g_{n,1}&\cdots&g_{n,m} \end{bmatrix} $$
即,将矩阵 视作 个长度为 的一维数列。设底为 ,模数为 ,则转移公式如下:
$$g_{x,y}=(\sum_{i=1}^xf_{i,y}\times p_2^{x-i})\operatorname{mod}M $$根据一维哈希的性质,将公式进行优化:
$$g_{x,y}=(g_{x-1,y}\times p_2+f_{x,y})\operatorname{mod}M $$说明
先按行和先按列都是一样的。总而言之,不管你先做什么,最终的式子代进去都是这样的:
$$g_{x,y}=(\sum_{i=1}^x\sum_{j=1}^ya_{i,j}\times p_2^{x-i}\times p_1^{y-j})\operatorname{mod}M $$依据总公式,仿照前缀和,你也可以在外面循环行,在里面循环列,同时求:
$$g_{x,y}=(a_{x,y}+g_{x-1,y}\times p_2+g_{x,y-1}\times p_1-g_{x-1,y-1}\times p_1\times p_2)\operatorname{mod}M $$这个公式就有一部分包含下文子矩阵的思想了。
第三步 获取哈希值
经过上面两个步骤后,矩阵 的右下角 就是矩阵 的哈希值。
另外,如果对上面的步骤保留矩阵, 数组的值也是 左上角子矩阵的哈希值,如图所示。
$$A=\begin{bmatrix} \color{orange}a_{1,1}&\color{orange}a_{1,2}&a_{1,3}&\cdots&a_{1,m}\\ \color{orange}a_{2,1}&\color{orange}a_{2,2}&a_{2,3}&\cdots&a_{2,m}\\ \color{orange}a_{3,1}&\color{orange}a_{3,2}&a_{3,3}&\cdots&a_{3,m}\\ a_{4,1}&a_{4,2}&a_{4,3}&\cdots&a_{4,m}\\ \vdots&\vdots&\vdots&\ddots&\vdots\\ a_{n,1}&a_{n,2}&a_{n,3}&\cdots&a_{n,m} \end{bmatrix}\longrightarrow G=\begin{bmatrix} g_{1,1}&g_{1,2}&g_{1,3}&\cdots&g_{1,m}\\ g_{2,1}&g_{2,2}&g_{2,3}&\cdots&g_{2,m}\\ g_{3,1}&\color{orange}g_{3,2}&g_{3,3}&\cdots&g_{3,m}\\ g_{4,1}&g_{4,2}&g_{4,3}&\cdots&g_{4,m}\\ \vdots&\vdots&\vdots&\ddots&\vdots\\ g_{n,1}&g_{n,2}&g_{n,3}&\cdots&g_{n,m} \end{bmatrix} $$在不发生冲突的情况下, 表示唯一的矩阵 。可以依据哈希值判断矩阵是否相同。
正确性证明
先对行进行哈希,再对列进行哈希,使得不同位置的元素有不同的权重,确保了较小的冲突概率。
需要知道的风险
在将较大的矩阵处理到较小的值域上时,冲突风险会加大。请选取适当的底和模数以减小出现问题的概率。
如果不放心,可以使用双模数哈希,或不使用二维哈希,考虑其它算法。代码实现
开
unsigned long long存储,使用自然溢出的方式取模。此时模数 。具体代码见下方例题。
子矩阵的哈希值获取
这一部分比较难以理解,建议自己算一下看看。
设行数在 到 之间、列数在 到 之间子矩阵的哈希值为 。
根据上述步骤,我们已经知道了所有的 ,其中 ,。参考一维哈希的方法,视作 进制数。不过这里将不同列视为 进制数(因为按行哈希时列转移乘了 ),将不同行视为 进制数。
这样,在原矩阵的右侧添加一列空列,矩阵的哈希值 变为 。
在原矩阵的下方添加一行空行,矩阵的哈希值 变为 。
感性理解一下,证明我不会。虽然矩阵是先对每一行哈希,再对每一列哈希,先以 为底再以 为底,但是最后乘上 依旧是左移。就相当于原本是以第 列做哈希,现在以第 列做哈希,先后顺序不影响。
结论
现在,我们需要 求解所有的 。根据上述结论,易推得:
$$\begin{aligned} H(x_1&,y_1,x_2,y_2)=\\ &\hspace{0.3cm}(H(1,1,x_2,y_2)&\small\text{全部}\\ &-H(1,1,x_1-1,y_2)\times p_2^{x_2-x_1+1}&\small\text{减上方}\\ &-H(1,1,x_2,y_1-1)\times p_1^{y_2-y_1+1}&\small\text{减左侧}\\ &+H(1,1,x_1-1,y_1-1)\times p_2^{x_2-x_1+1}\times p_1^{y_2-y_1+1}&\small\text{加左上角}\\ &\hspace{0.3cm})\operatorname{mod}M\\ \end{aligned} $$画出来是这样的:
$$\begin{bmatrix} \color{purple}g_{1,1}&\color{purple}\cdots&\color{purple}g_{1,x_1-1}&\color{red}g_{1,x_1}&\color{red}\cdots&\color{red}g_{1,x_2}&\color{darkgrey}\cdots&\color{darkgrey}g_{1,m}\\ \color{purple}\vdots&\color{purple}\ddots&\color{purple}\vdots&\color{red}\vdots&\color{red}\ddots&\color{red}\vdots&\color{darkgrey}\ddots&\color{darkgrey}\vdots\\ \color{purple}g_{y_1-1,1}&\color{purple}\cdots&\color{purple}g_{y_1-1,x_1-1}&\color{red}g_{y_1-1,x_1}&\color{red}\cdots&\color{red}g_{y_1-1,x_2}&\color{darkgrey}\cdots&\color{darkgrey}g_{y_1-1,m}\\ \color{blue}g_{y_1,1}&\color{blue}\cdots&\color{blue}g_{y_1,x_1-1}&\color{green}g_{y_1,x_1}&\color{green}\cdots&\color{green}g_{y_1,x_2}&\color{darkgrey}\cdots&\color{darkgrey}g_{y_1,m}\\ \color{blue}\vdots&\color{blue}\ddots&\color{blue}\vdots&\color{green}\vdots&\color{green}\ddots&\color{green}\vdots&\color{darkgrey}\ddots&\color{darkgrey}\vdots\\ \color{blue}g_{y_2,1}&\color{blue}\cdots&\color{blue}g_{y_2,x_1-1}&\color{green}g_{y_2,x_1}&\color{green}\cdots&\color{green}g_{y_2,x_2}&\color{darkgrey}\cdots&\color{darkgrey}g_{y_2,m}\\ \color{darkgrey}\vdots&\color{darkgrey}\ddots&\color{darkgrey}\vdots&\color{darkgrey}\vdots&\color{darkgrey}\ddots&\color{darkgrey}\vdots&\color{darkgrey}\ddots&\color{darkgrey}\vdots\\ \color{darkgrey}g_{m,1}&\color{darkgrey}\cdots&\color{darkgrey}g_{m,x_1-1}&\color{darkgrey}g_{m,x_1}&\color{darkgrey}\cdots&\color{darkgrey}g_{m,x_2}&\color{darkgrey}\cdots&\color{darkgrey}g_{n,m} \end{bmatrix} $$其中,绿色为目标区域,(绿色+红色+蓝色+紫色)为全部区域,(红色+紫色)为上方区域,(蓝色+紫色)为左侧区域,紫色为重复的左上角区域,灰色为无关区域。
为什么要乘
此处的颜色标记与上面的不同,请注意区分。
下面的矩阵在哈希意义下,不是普通的矩阵,详细规则见上。请将矩阵视作哈希。
为方便理解,矩阵中的 记作 以标识位置。
下文证明及其不严谨,仅作理解。首先,左侧区域原本是这样的:
$$A=\begin{bmatrix} \color{blue}g_{1,1}&\color{blue}\cdots&\color{blue}g_{1,x_1-1}\\ \color{blue}\vdots&\color{blue}\ddots&\color{blue}\vdots\\ \color{blue}g_{y_2,1}&\color{blue}\cdots&\color{blue}g_{y_2,x_1-1} \end{bmatrix} $$整体长这样:
$$C=\begin{bmatrix} \color{blue}g_{1,1}&\color{blue}\cdots&\color{blue}g_{1,x_1-1}&\color{green}g_{1,x_1}&\color{green}\cdots&\color{green}g_{1,x_2}\\ \color{blue}\vdots&\color{blue}\ddots&\color{blue}\vdots&\color{green}\vdots&\color{green}\ddots&\color{green}\vdots\\ \color{blue}g_{y_2,1}&\color{blue}\cdots&\color{blue}g_{y_2,x_1-1}&\color{green}g_{y_2,x_1}&\color{green}\cdots&\color{green}g_{y_2,x_2} \end{bmatrix} $$我们想要得到:
$$B=\begin{bmatrix} \color{green}g_{1,x_1}&\color{green}\cdots&\color{green}g_{1,x_2}\\ \color{green}\vdots&\color{green}\ddots&\color{green}\vdots\\ \color{green}g_{y_2,x_1}&\color{green}\cdots&\color{green}g_{y_2,x_2} \end{bmatrix} $$注意,在上文所述的哈希(视作 、 进制)中,可以向上方和左侧补前导零。所以有:
$$B=\begin{bmatrix} \color{green}g_{1,x_1}&\color{green}\cdots&\color{green}g_{1,x_2}\\ \color{green}\vdots&\color{green}\ddots&\color{green}\vdots\\ \color{green}g_{y_2,x_1}&\color{green}\cdots&\color{green}g_{y_2,x_2} \end{bmatrix}=\begin{bmatrix} \color{darkgrey}0_{1,1}&\color{darkgrey}\cdots&\color{darkgrey}0_{1,x_1-1}&\color{green}g_{1,x_1}&\color{green}\cdots&\color{green}g_{1,x_2}\\ \color{darkgrey}\vdots&\color{darkgrey}\ddots&\color{darkgrey}\vdots&\color{green}\vdots&\color{green}\ddots&\color{green}\vdots\\ \color{darkgrey}0_{y_2,1}&\color{darkgrey}\cdots&\color{darkgrey}0_{y_2,x_1-1}&\color{green}g_{y_2,x_1}&\color{green}\cdots&\color{green}g_{y_2,x_2} \end{bmatrix} $$但不能在下方或右侧补零,因为这样改变了哈希大小。所以:
$$A=\begin{bmatrix} \color{blue}g_{1,1}&\color{blue}\cdots&\color{blue}g_{1,x_1-1}\\ \color{blue}\vdots&\color{blue}\ddots&\color{blue}\vdots\\ \color{blue}g_{y_2,1}&\color{blue}\cdots&\color{blue}g_{y_2,x_1-1} \end{bmatrix}\neq\begin{bmatrix} \color{blue}g_{1,1}&\color{blue}\cdots&\color{blue}g_{1,x_1-1}&\color{darkgrey}0_{1,x_1}&\color{darkgrey}\cdots&\color{darkgrey}0_{1,x_2}\\ \color{blue}\vdots&\color{blue}\ddots&\color{blue}\vdots&\color{darkgrey}\vdots&\color{darkgrey}\ddots&\color{darkgrey}\vdots\\ \color{blue}g_{y_2,1}&\color{blue}\cdots&\color{blue}g_{y_2,x_1-1}&\color{darkgrey}0_{y_2,x_1}&\color{darkgrey}\cdots&\color{darkgrey}0_{y_2,x_2} \end{bmatrix} $$因此,我们不能直接将 作为目标 的答案,因为 没有与 对齐。
$$A\times p_2^{x_2-x_1+1}= \begin{bmatrix} \color{blue}g_{1,1}&\color{blue}\cdots&\color{blue}g_{1,x_1-1}&\color{darkgrey}0_{1,x_1}&\color{darkgrey}\cdots&\color{darkgrey}0_{1,x_2}\\ \color{blue}\vdots&\color{blue}\ddots&\color{blue}\vdots&\color{darkgrey}\vdots&\color{darkgrey}\ddots&\color{darkgrey}\vdots\\ \color{blue}g_{y_2,1}&\color{blue}\cdots&\color{blue}g_{y_2,x_1-1}&\color{darkgrey}0_{y_2,x_1}&\color{darkgrey}\cdots&\color{darkgrey}0_{y_2,x_2} \end{bmatrix} $$
要想对齐,就要将 左移 列,也就是在 的右边插入 个空列。按照上面的证明方式,只需将 的哈希值乘 即可。这样, 与 对齐后我们就可以相减得到答案。
$$\begin{aligned}B=&\begin{bmatrix} \color{green}g_{1,x_1}&\color{green}\cdots&\color{green}g_{1,x_2}\\ \color{green}\vdots&\color{green}\ddots&\color{green}\vdots\\ \color{green}g_{y_2,x_1}&\color{green}\cdots&\color{green}g_{y_2,x_2} \end{bmatrix}=\begin{bmatrix} \color{darkgrey}0_{1,1}&\color{darkgrey}\cdots&\color{darkgrey}0_{1,x_1-1}&\color{green}g_{1,x_1}&\color{green}\cdots&\color{green}g_{1,x_2}\\ \color{darkgrey}\vdots&\color{darkgrey}\ddots&\color{darkgrey}\vdots&\color{green}\vdots&\color{green}\ddots&\color{green}\vdots\\ \color{darkgrey}0_{y_2,1}&\color{darkgrey}\cdots&\color{darkgrey}0_{y_2,x_1-1}&\color{green}g_{y_2,x_1}&\color{green}\cdots&\color{green}g_{y_2,x_2} \end{bmatrix}\\=&\begin{bmatrix} \color{blue}g_{1,1}&\color{blue}\cdots&\color{blue}g_{1,x_1-1}&\color{green}g_{1,x_1}&\color{green}\cdots&\color{green}g_{1,x_2}\\ \color{blue}\vdots&\color{blue}\ddots&\color{blue}\vdots&\color{green}\vdots&\color{green}\ddots&\color{green}\vdots\\ \color{blue}g_{y_2,1}&\color{blue}\cdots&\color{blue}g_{y_2,x_1-1}&\color{green}g_{y_2,x_1}&\color{green}\cdots&\color{green}g_{y_2,x_2} \end{bmatrix}-\begin{bmatrix} \color{blue}g_{1,1}&\color{blue}\cdots&\color{blue}g_{1,x_1-1}&\color{darkgrey}0_{1,x_1}&\color{darkgrey}\cdots&\color{darkgrey}0_{1,x_2}\\ \color{blue}\vdots&\color{blue}\ddots&\color{blue}\vdots&\color{darkgrey}\vdots&\color{darkgrey}\ddots&\color{darkgrey}\vdots\\ \color{blue}g_{y_2,1}&\color{blue}\cdots&\color{blue}g_{y_2,x_1-1}&\color{darkgrey}0_{y_2,x_1}&\color{darkgrey}\cdots&\color{darkgrey}0_{y_2,x_2} \end{bmatrix}\\=&C-A\times p_2^{x_2-x_1+1}\end{aligned}\\ H(B)=(H(C)-H(A)\times p_2^{x_2-x_1+1})\operatorname{mod}M $$同样地,通过位移操作,我们可以完成上方和左上角的矩阵操作,只需要对应乘 、 的幂即可。
在程序中,由于会频繁进行 、 的幂运算,我们通常会预处理出它们的 次方,并存储到
unsigned long long数组中。
举例
,,。
$$A=\begin{bmatrix} 1&2&3\\4&5&6 \end{bmatrix},\ F=\begin{bmatrix} 1&9&66\\4&33&237 \end{bmatrix},\ G=\begin{bmatrix} 1&9&66\\ 143&2184&16011 \end{bmatrix}\\ \text{对于子矩阵:} A'=\begin{bmatrix} 2&3\\5&6 \end{bmatrix},\ F'=\begin{bmatrix} 2&17\\5&41 \end{bmatrix},\ G'=\begin{bmatrix} 2&17\\483&4104 \end{bmatrix} $$验证:,成立。
例题
洛谷P10474 [BeiJing2011] Matrix 矩阵哈希
注:为方便书写,本文将题目中的 和 替换为了 和 ,与原题目有出入。题目描述
给出一个 行 列的 矩阵,以及 个 行 列的 矩阵,你需要求出这 个矩阵中有哪些矩阵是大矩阵的子矩阵。
数据范围:,。
思路
先对整体取哈希,把每个大小为 的存入
unordered_map;然后对每个子矩阵取哈希,判断是否在map中即可。时间复杂度 。代码
AC 49.65MB 1.08s
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <unordered_map> using namespace std; typedef long long ll; typedef unsigned long long ull; char buf[1<<20], *p1, *p2; #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++) inline ll read() { ll x=0, f=1; char ch=getchar(); while (ch<'0'||ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48), ch=getchar(); return x*f; } #define N 1010 int n, m, A, B, q; ull a[N][N]; unordered_map<ull, bool> mp; namespace tdh { #define P1 503ULL #define P2 1013ULL int n, m; ull f[N][N], g[N][N]; ull p1[N], p2[N]; void init(ull d[N][N], int n, int m) { p1[0]=p2[0]=1; for (int i=1; i<N; ++i) p1[i]=p1[i-1]*P1, p2[i]=p2[i-1]*P2; for (int i=1; i<=n; ++i) for (int j=1; j<=m; ++j) { f[i][j]=f[i][j-1]*P1+d[i][j]; } for (int j=1; j<=m; ++j) for (int i=1; i<=n; ++i) { g[i][j]=g[i-1][j]*P2+f[i][j]; } } ull get(int x1, int y1, int x2, int y2) { return g[x2][y2]-g[x1-1][y2]*p2[x2-x1+1]-g[x2][y1-1]*p1[y2-y1+1] +g[x1-1][y1-1]*p1[y2-y1+1]*p2[x2-x1+1]; } } signed main() { // freopen("a.in", "r", stdin); n=read(), m=read(), A=read(), B=read(); for (int i=1; i<=n; ++i) for (int j=1; j<=m; ++j) { char ch=getchar(); while (ch<'0'||ch>'1') ch=getchar(); a[i][j]=ch-'0'; } tdh::init(a, n, m); for (int i=1; i<=n-A+1; ++i) for (int j=1; j<=m-B+1; ++j) { mp[(int)tdh::get(i, j, i+A-1, j+B-1)]=1; } q=read(); while (q--) { for (int i=1; i<=A; ++i) for (int j=1; j<=B; ++j) { char ch=getchar(); while (ch<'0'||ch>'1') ch=getchar(); a[i][j]=ch-'0'; } tdh::init(a, A, B), printf("%d\n", mp[(int)tdh::get(1,1,A,B)]); } return 0; }
写在后面
使用 的方式表示行列真的不标准,大家不要学我(
表示行内(不同 之间)的底, 表示列内(不同 之间)的底,为方便理解可自行替换为 、。这是蒟蒻的第一篇题解(严格意义上第二篇,因为第一篇没过审),管理员大大手下留情。
本人高一,因实力太菜马上就要退役了,忽然发现自己一篇题解还没写……于是就有了这篇题解。这个算法很冷,学了很大概率也用不上,随缘吧。
最后安利一下自己的 Blog QWQ
- 1
信息
- ID
- 10094
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者