1.线段树维护哈希
主要难点在于合并和pushup
首先我们明确哈希的计算原理。对于一个字符串 $S = s_1 s_2 s_3 \dots s_n$,它的哈希 lnum 计算公式为:
$Hash(S) = s_1 \cdot base^0 + s_2 \cdot base^1 + s_3 \cdot base^2 + \dots + s_n \cdot base^{n-1}$
一个字符就是$s[i]-'a'+1$
接下来我们合并两个相邻的子串:
左子串 $L$,长度为 $len_L$,哈希值为 $Hash(L)$
右子串 $R$,长度为 $len_R$,哈希值为 $Hash(R)$
合并后的总字符串为 $L + R$。根据上面的定义,总哈希值应该是:
$$Hash(L+R) = L_1 \cdot base^0 + \dots + L_{len_L} \cdot base^{len_L-1} + R_1 \cdot base^{len_L} + \dots + R_{len_R} \cdot base^{len_L + len_R - 1}$$
我们将这个式提取公因式,分为两部分:
$$Hash(L+R) = \underbrace{(L_1 \cdot base^0 + \dots + L_{len_L} \cdot base^{len_L-1})}_{\text{正好是 } Hash(L)} + \underbrace{base^{len_L} \cdot (R_1 \cdot base^0 + \dots + R_{len_R} \cdot base^{len_R-1})}_{\text{正好是 } base^{len_L} \cdot Hash(R)}$$
所以
$$Hash(L+R) = Hash(L) + Hash(R) \cdot base^{len_L}$$
code:
res.lnum = (ld.lnum + (pw[ld.r - ld.l + 1] * rd.lnum) % mod) % mod;
有的时候我们还要反过来算一遍,还是列出反向后的柿子
$$rev(L+R) = rev(R) + rev(L)$$
注意$R$在$L$前面
所以现在,我们要计算 $rev(R) + rev(L)$ 的哈希。套用我们刚刚推导出的合并方法,有:
$$rev(L+R) = Hash(rev(R)) + Hash(rev(L)) \cdot base^{len_{rev(R)}}$$
显然有
$$revHash(L+R) = revHash(R) + revHash(L) \cdot base^{len_r}$$
故
$$rnum(L+R) = rnum(R) + rnum(L) \cdot base^{len_R}$$
code:
res.rnum = (rd.rnum + (pw[rd.r - rd.r + 1] * ld.rnum) % mod) % mod;
我们感性理解一下,其实核心在于字符位置的变化。位置的变化会导致$Hash(S) = s_1 \cdot base^0 + s_2 \cdot base^1 + s_3 \cdot base^2 + \dots + s_n \cdot base^{n-1}$中$base$的变化。而其中唯一可能变化的量就是$base$。所以我们找$base$何时变化就可以了。那base何时变化呢?随着位次的的增加而变化。所以我们考虑字符所在的位次就可以了。
1.正向合并:左串不动,右串的每一位都要往后挪 $len_L$ 个位置,所以右串要乘 $base^{len_L}$。
2.反向合并:因为整体反转了,右串跑到了前面,左串跑到了后面。所以左串的每一位要往后挪 $len_R$ 个位置,所以左串要乘 $base^{len_R}$。而右串原本的hash计算$base$就是从$1$开始的,现在还是从$1$开始,所以不用考虑跑到前面带来的影响!
2.靠进位判断大小关系
取模其实是一个轮回,对要取模的数进行除法操作,若结果大于$1$那就说明进位了,数字会变小。
3.雷霆bitset
其实就是vector<bool>牛逼的版本
看到「在模 2 意义下」,可以想到用 bitset 维护每个可重集.
P8520休闲娱乐的题解
对于长椅分配问题,可以考虑把整个地图隔$1$黑白染色,这样可以区分开横着和竖着的限制
然后我们在尽量把每条路分配给左边,上边的长椅。尽量让他们“居居”在左上角一个有限的区间,可以证明这是最优方案
怎么证明呢?我不会。