矩阵哈希

矩阵哈希

矩阵哈希可以解决一系列消消乐问题,即:

给定一个序列 \(A\) ,每次可以消除相邻相同两项,问是否可以消除完。

这一类问题。


做法

我们对每个字符 \(c\) 随机一个矩阵 \(M_c\),那么当出现奇数次的时候乘 \(M_c\) ,出现偶数次的时候乘他的逆 \(M_c^{-1}\) 。那么当一个序列里的所有数按顺序乘起来为 \(I\) 的时候,此时这个序列就可以被消掉了。

那为啥不能用数呢?

容易发现矩阵和数的最大区别就是不具备交换律。

具体地例子是:

\(ABAB\) ,这个在矩阵的时候是 \(M_A\cdot M_B\cdot M_A^{-1}\cdot M_B^{-1}\), 很显然乘起来并不是 \(I\) ,但是当他是数的时候就寄了。

这时候我们就知道矩阵的牛逼之处了:

由于不具备交换律,那么此时由于只具有结合律,这种类似括号匹配的事情就可以轻松处理。


更多的,我们可以设置 \(M_c=M_c^{-1}\),这样就不用考虑奇偶了。

我们设 \(|M_c|=D\) ,那么

\({\begin{bmatrix}a,b\\c,d\end{bmatrix}}^{-1}=\begin{bmatrix}\frac{-d}{D},\frac{b}{D}\\\frac{c}{D},\frac{-a}{D}\end{bmatrix}\)

只要满足 \(D=1\)\(a+d=0\) 即可。

随机出 \(a\)\(b\) ,那么 \(d=-a\)\(c=\frac{ad-1}{b}\)


群论

这其实是从群论角度来理解这个问题。

在这里我们将补充一些群论知识:

群的基本定义

我们首先从集合 \(S\) 出发,一步步通过增添性质来构造群。

首先加入运算符 \(*\) ,那么此时我们就有了一个二元组 \((S,*)\)

但是如果随便给一个运算符 \(*\) ,可能会带来一些不好的影响,比如我们在 \(S\) 中任意选了两个元素 \(a,b\)\(a*b\notin S\) ,那这样就很不方便了。

为了方便,我们让 \(*\) 具有封闭性,即 \(\forall a,b\in S ,a*b\in S\)

那么当我们将封闭性这一条性质加入时:

我们得到了一个原群(magma)!

更进一步,我们在这里只考虑了2个元素的运算,那多个元素呢?

考虑 \(a,b,c\in S\) ,那我们要怎么计算这三者的运算呢?

如果\((a*b)*c\neq a*(b*c)\) 的话,那么此时 \(a*b*c\) 如何计算我们将一头雾水。

那么当我们将结合律这一条性质加入时:

我们得到了一个半群(semi group)!

这就是半群的定义:对于一个二元组 \((S,*)\) ,如果 \(*\) 作用在 \(S\) 上具有封闭性和结合律,那么这个二元组就被称为半群。

现在我们考虑再加入一些东西,继续升级我们的结构。

我们将目光放在集合中的元素上:

考虑找到一个最优秀的元素:\(e\)

如果我们能做到让这个 \(e*a=a\)\(a*e=a\) ,那么此时我们更进一步,得到了幺元。

那么当我们将幺元这元素加入时:

我们得到了一个幺半群(monoid)!

继续向下探索,如果我们都有了幺元 \(e\) 了,那么对于集合 \(S\) 中的一个元素 \(a\) ,如果能找到一个元素 \(b\) 使得 \(a*b=b*a=e\) ,那么此时我们就称 \(b\)\(a\) 的逆元。

那么如果 \(\forall a\in S\) ,我们都能找到 \(b\in S,a*b=b*a=e\) ,那么我们就又得到了一条新性质:可逆性。

那么当我们将可逆性这条性质加入时:

我们得到了一个群(group)!

至此,我们终于构造出了我们的主角:群。

总结一下,对于群 \((S,*)\) 而言,我们有4条性质

  • 封闭性:\(\forall a,b\in S,a*b=c\in S\)
  • 结合律:\(\forall a,b,c\in S,(a*b)*c=a*(b*c)\)
  • 幺元:\(\exists e, \forall a\in S,a*e=e*a=a\)
  • 可逆性:\(\forall a\in S,\exists b,a*b=b*a=e\)

我们回顾一下小学一年级学的算数的那么多性质,发现我们还漏了一条:交换律。

那么当我们将可逆性这条性质加入时:

我们得到了一个阿贝尔群(abelian group)!
群的性质

我们现在已经得到了好多好多东西,那么我们将我们的得到的结构拿出来玩一玩,看看是否能得到一些有趣的结果。

在这里我们只讨论群的一些基本性质:

  • 对于群 \(G\) 而言,有且只有一个幺元。
    • 证明是简单的:
      • 考虑反证法:假设我们有两个不同幺元 \(e,e'\) ,那么 \(e*e'=e=e'*e=e'\),此时就与不同这一限制互逆了,因此矛盾,我们只有一个逆元。
  • 对于群 \(G\) 而言,\(\forall a\in S\) ,有且仅有一个元素 \(b\in S\) ,使得 \(a*b=e\)
    • 证明同样是简单的:
      • 同样考虑反证法::假设我们有两个不同逆元 \(b,b'\) ,那么 \(b=e*b=(b'*a)*b=b'*(a*b)=b'\),此时就与不同这一限制互逆了,因此矛盾,我们只有一个逆元。
  • 对于群 \(G\) 而言,\(\forall a,b\in S\) ,有解且有唯一解 \(x\)\(x*a=b\) ,且有解且有唯一解 \(y\)\(a*y=b\)
    • 证明与上述类似,这里就不再阐述了
  • 对于群 \(G\) 而言,\(\forall a,b\in S,(a^{-1})^{-1}=a,(a*b)^{-1}=b^{-1}*a^{-1}\)
    • 由上一条可以推出答案是唯一的,验证正确性只需要运用结合律即可。

至此,我们论述完了群的一些基本性质。

回过头来看我们的做法,可以发现我们的矩阵和矩阵乘法完美的构成了一个非阿贝尔群。

那么也就是说,如果我们能找到另一种 \((S,*)\) 来替代 \((M,*)\) , 其实也是能做到哈希的。

但是为了方便,一般情况下都考虑矩阵。


例题

具体的,我们来看一些例题

CSP-S2023 T2:

问有多少连续子串可以被消空。

有个原题是:Problem - 1223F - Codeforces

就是把字符集变大了,用矩阵哈希一样做。

那么从前往后计算前缀和,只要记录有多少个 \(sum_j=sum_i^{-1}\) 即可,这个直接扫一遍 map 存一下就行。

存 map 只要定义 \(<\)\(=\) 即可。


模拟T4:

求有多少种方案可以使得交换不同的两个字符使得最后可以被消除。

一种复杂度更劣的做法是考虑 cdq 分治:

那么枚举交换的是哪个字符,计算出左端点前面的值和到 mid的值,用 \(map\) 存一下,再考虑右端点就行。

这样复杂度是 \(O(n\log^2n)\)

另一种更优的方法是:

直接考虑从后往前扫,那么每次新加一个字符相当于对目前的 \(map\) 整体在右乘一个矩阵,用一个 tag 维护一下就行。插入新矩阵就先右乘一下逆即可。复杂度是 \(O(n\log n)\)


Flower's Land

这个好像才是刚开始的出处?(是花花的题好像

两种操作,一种是区间值域右移:\(a_i=(a_i+1)\mod 3\),另一种是问区间是否可以被消除。

做法就考虑线段树,提前维护好线段树上每个区间右移时候的值,那么只要修改 tag 就行了。

询问是简单的。

posted @ 2023-11-07 11:20  Hawkrad  阅读(361)  评论(0)    收藏  举报