2022.11.25 闲话
JS 真好玩
JavaScript 中 ==
运算的一些结果:
0 |
[] |
"" |
'' |
"0" |
'0' |
[''] |
['0'] |
[""] |
[[]] |
|
---|---|---|---|---|---|---|---|---|---|---|
0 |
true |
true |
true |
true |
true |
true |
true |
true |
true |
true |
[] |
true |
false |
true |
true |
false |
false |
false |
false |
false |
false |
"" |
true |
true |
true |
true |
false |
false |
true |
false |
true |
true |
'' |
true |
true |
true |
true |
false |
false |
true |
false |
true |
true |
"0" |
true |
false |
false |
false |
true |
true |
false |
true |
false |
true |
'0' |
true |
false |
false |
true |
true |
true |
false |
true |
false |
true |
[''] |
true |
false |
true |
true |
false |
false |
false |
false |
false |
false |
['0'] |
true |
false |
false |
false |
true |
true |
false |
false |
false |
false |
[""] |
true |
false |
true |
true |
false |
false |
false |
false |
false |
false |
[[]] |
true |
false |
true |
true |
false |
false |
false |
false |
false |
false |
这些应该比较有代表性,你也可以试试 0.0
,[0]
,[[[[[[]]]]]]
," "
(至少这些都是等于 0
的),或者可以试试它们的加法运算等等(0 + [0] = "0"
.jpg).
至少 JS 的 ==
还是有交换律的,挺好 .
正文:
搁浅
久未放晴的天空
依旧留着你的笑容
哭过 却无法掩埋歉疚
风筝在阴天搁浅
想念还在等待救援
我拉着线 复习你给的温柔
曝晒在一旁的寂寞
笑我给不起承诺
怎么会 怎么会
你竟原谅了我
我只能永远读着对白
读着我给你的伤害
我原谅不了我
就请你当作我已不在
我睁开双眼看着空白
忘记你对我的期待
读完了依赖 我很快就离开
久未放晴的天空
依旧留着你的笑容
哭过 却无法掩埋歉疚
风筝在阴天搁浅
想念还在等待救援
我拉着线 复习你给的温柔
曝晒在一旁的寂寞
笑我给不起承诺
怎么会 怎么会
你竟原谅了我
我只能永远读着对白
读着我给你的伤害
我原谅不了我
就请你当作我已不在
我睁开双眼 看着空白
忘记你对我的期待
读完了依赖 我很快就
我只能永远读着对白
读着我给你的伤害
我原谅不了我
就请你当作我已不在
我睁开双眼看着空白
忘记你对我的期待
读完了依赖 我很快就离开
好了正文结束 .
大体贺的 CTSC2017 国家候选队论文 徐明宽《非常规大小分块算法初探》.
以下时间复杂度若无特殊说明均指最坏时间复杂度 .
最优块长取法
简介
假装问题中只有一个常量 \(n\),则分块算法的时间复杂度大概可以表示为
其中 \(B\) 是块长,\(f_i\) 全部关于 \(B\) 单调增或减 .
目标:找到 \(B\) 使得 \(T(n,B)\) 最小 .
\(k=2\) 情况
一种常见情形:分块算法的时间复杂度形如
其中 \(f\) 关于 \(B\) 单调增,\(g\) 关于 \(B\) 单调减 .
一个常见想法是取使得 \(f(n,B)=g(n,B)\) 的 \(B\),这样 \(T(n,B)\) 是最小的 .
这个思路在渐进情况下是没啥问题的,具体的,显然 \(\Theta(T(n,B))=\Theta(f(n,B)+g(n,B))=\Theta(\max\{f(n,B),g(n,B)\})\),根据单调性,最小化 \(\max\{f(n,B),g(n,B)\}\) 显然就取 使得 \(f(n,B)=g(n,B)\) 的 \(B\) 点,这样就证完了 .
如果想更精细一点可以发现 \(T(n,B)\) 关于 \(B\) 单谷,则三分导一下即可 .
几个例子:
树状数组 1
维护一个序列 \(\{a_n\}\),\(q\) 次询问,支持:
1 x v
,将 \(a_x\gets a_x+v\) .2 l r
,求 \(\displaystyle\sum_{i=l}^ra_i\) .
两种想法:
第一种就是平凡序列分块,分 \(B\) 块,处理块内前缀和,块间暴力求和,则一次查询的复杂度由块间暴力求和贡献,就是 \(\Theta\left(\dfrac nB\right)\) 的,一次修改需要更新块内前缀和,所以也是 \(\Theta(B)\) 的 .
于是时间复杂度就是 \(\Theta\left(q\left(B+\dfrac nB\right)\right)\),显而易见当 \(B=\dfrac nB\) 时 \(B=\sqrt n\),于是取 \(B=\Theta(\sqrt n)\),得最优时间复杂度 \(\Theta(q\sqrt n)\) .
根据均值不等式可以得到 \(B+\dfrac nB\) 的最小值确实是 \(\sqrt n\) .
上面那个算法交换块内和块间的处理方式也行,分析完全一致 .
或者考虑把询问分块或者叫根号重构,处理出初始前缀和,把每次修改存起来,查询的时候扫一遍算贡献,每 \(B\) 次把修改实施到前缀和上,则时间复杂度为 \(\Theta\left(qB+n\cdot \dfrac qB\right)\),其实和上面的式子完全一样,类似可以得到最优时间复杂度是 \(\Theta(q\sqrt n)\) .
一个 \(B\) 最优不是 \(\sqrt n\) 的例子:
分块入门 2
维护一个序列 \(\{a_n\}\),\(q\) 次询问,支持:
1 l r v
,对于所有 \(x\in[l,r]\),将 \(a_x\gets a_x+v\) .2 l r k
,求 \(\displaystyle\sum_{i=l}^r[a_i<k]\) .
块内排序,区间加考虑不对整块的相对大小产生影响所以直接打标记,散块暴力重构即可,查询就二分一下 .
预处理 \(O(n\log n)\),询问是 \(\Theta\left(\dfrac{n\log B}B+B\right)\) 的,修改是 \(\Theta\left(\dfrac nB+B\log B\right)\) 的,平衡一下可以得到 \(B=\sqrt n\) 时取到最优时间复杂度 \(\Theta(q\sqrt n\log n)\) .
优化一下:散块暴力重构其实可以不 \(\Theta(B\log B)\) 暴力,注意到区间加之后序列分为两个有序部分,归并一下就是 \(\Theta(B)\) 的 .
总时间复杂度 \(\Theta\left(q\left(\dfrac{n\log B}B+B\right)\right)\),令 \(\dfrac{n\log B}B=B\) 可以解得 \(B=\sqrt{n\log n}\),此时时间复杂度为 \(\Theta(q\sqrt{n\log n})\) .
用均值不等式同样可以得到 \(\dfrac{n\log B}B+B\ge 2\sqrt{n\log n}\) .
项虽然看起来比较多但是 \(\dfrac nB\) 严格打不过 \(\dfrac{n\log B}B\),所以可以减一项,这样就两项了 .
带修莫队:直接放时间复杂度:\(\Theta\left(\dfrac n{B^2}+B\right)\) .
两种做法:
(三元)均值不等式做法:
直接令 \(\dfrac n{B^2}=B\),则解得 \(B=n^{2/3}\),代入就是 \(T(n,B)=\Theta(n^{2/3})\),挺好 .
\(k>2\) 情形
其实就是 \(k=2\) 情形的推广 .
解 \(f_1(n,B)=f_i(n,B)\) 其中 \(i>1\),然后得到 \(k-1\) 个解,一个个代进去试试总有一个对的 .
这个无脑做法还是非常容易用的,正确性和 \(k=2\) 证明方法基本相同 .
xmk 论文的三个例子:
\(T(n,B)\) | 最优的 \(B\) | 最优的 \(T(n,B)\) |
---|---|---|
\(\Theta\left(\dfrac{nq\log n}B+nB\right)\) | \(\Theta(\sqrt{q\log n})\) | \(\Theta(n\sqrt{q\log n})\) |
\(\Theta\left(\dfrac{n^3}{B^2w}+qB\right)\) | \(\Theta\left(\dfrac{n}{\sqrt[3]{qw}}\right)\) | \(\Theta\left(\dfrac{nq^{2/3}}{w^{1/3}}\right)\) |
\(\Theta\left(\dfrac{nq\log n}B+nB\right)\) | \(\Theta(n^{2/3})\) | \(\Theta(n^{2/3})\) |
常数分析
这个渐进的有时候不如随便选一个 \(B=\sqrt n\) 跑得快,同样某些题还需要反复提交来找出实际上跑得最快的块长 \(B\) .
这表明渐进的时间复杂度分析已经不足了,尝试分析一下常数 .
一种做法是精细计算,这个就不说了 .
一种做法是毛估估一下,看看哪边常数小就给它分点复杂度过去,这个可以按单调性分析 .
反正挺玄学的 .
可以看一下论文里写的 wys 著名题目挑战 Task 2 的分块 FFT 如何卡常卡到 \(1\text{s}\) 以内的 .
非常规大小分块算法应用
?这两个 H2 有啥关系
Eratosthenes 筛空间复杂度优化
众所周知渐进分析一则:
一个常见 Trick:要对 \(1\dots n\) 所有数分解质因数,则枚举不大于 \(n\) 的素数 \(p\) 暴力把它的所有不超过 \(n\) 的倍数加上因子 \(p\),这样时间复杂度就是 \(\Theta(n\log\log n)\) 的 .
平凡推广:对于区间 \((L,R]\),如果已经知道了所有不超过 \(\sqrt R\) 的素数那么可以在 \(\Theta\left(\dfrac{\sqrt R}{\log R}+(R-L)\log\log R\right)\) 的时间复杂度内分解 \((L,R]\) 的所有数 .
于是把 \([1,n]\) 分块,块长为 \(B\),然后做 Eratosthenes 筛,则时间复杂度就是
空间复杂度 \(\Theta\left(\dfrac{\sqrt n}{\log n}+B\right)\),滚一下 .
取 \(B=\dfrac{\sqrt n}{\log n}\),时间复杂度还是 \(\Theta(n\log\log n)\),空间复杂度变成 \(\Theta\left(\dfrac{\sqrt n}{\log n}\right)\) .
这样可能可以把数组放到 L1 或 L2 里,按理说跑得会更快一点吧 .
快速 01 矩阵乘法
给 \(A_{m\times n},B_{n\times k}\),求 \(C_{m\times k}\) 满足:
\[C_{i,j}=\left(\sum_{k'=1}A_{i,k'}B_{k',j}\right)\bmod 2 \]
下面的时间复杂度都取一个 \(n=m=p\) 情况的,方便观察比较 .
一个显然做法:bitset 优化暴力,时间复杂度 \(\Theta(mn+nm+mp+nmp/w)\),\(n=m=p\) 时时间复杂度为 \(\Theta(n^3/w)\) .
令块长为 \(B\),将 \(m,n,k\) 都补成 \(B\) 的倍数,则可以将 \(A,B,C\) 分成若干 \(B\times B\) 的子矩阵 .
两个 \(B\times B\) 矩阵总共有 \(4^{B^2}\) 种情况,用 \(\Theta(4^{B^2}B^3)\) 的时间复杂度暴力处理出所有答案 .
然后可以在 \(\Theta(mn+np)\) 的时间复杂度内找到 \(A,B\) 对应块的答案,\(\Theta(nB)\) 时间复杂度内投到 \(C\) 的对应块上 .
这样的时间复杂度就是 \(\Theta\left(4^{B^2}B^3+mn+np+mp+\dfrac{nmp}B\right)\),取 \(B=\sqrt{\dfrac{\log(nmp)}3}\) 得到最优时间复杂度 \(\Theta\left(nm+mp+np+\dfrac{nmp}{\sqrt{\log(nmp)}}\right)\),当 \(n=m=p\) 时时间复杂度为 \(\Theta\left(\dfrac{n^3}{\sqrt{\log n}}\right)\) .
在矩阵乘法的数据范围里 \(\sqrt{\log n}\) 一般都远远小于 \(w=64\),所以这个算法跑得还没有 bitset 快 /hsh
考虑把它俩结合一下,处理所有 \(B\times B\) 矩阵乘法的 \(4^{B^2}\) 种情况的时候用 bitset 优化一下,这样就变成 \(\Theta(4^{B^2}B^3/w)\) 的时间复杂度,投到 \(C\) 矩阵的过程也可以 bitset 优化到 \(\Theta(nB/w)\),这样总时间复杂度就是 \(\Theta\left(4^{B^2}B^3/w+mn+np+mp+\dfrac{nmp}{Bw}\right)\),取 \(B=\sqrt{\dfrac{\log(nmp)}3}\) 得到最优时间复杂度 \(\Theta\left(nm+mp+np+\dfrac{nmp}{\sqrt{w\log(nmp)}}\right)\),当 \(n=m=p\) 时时间复杂度为 \(\Theta\left(\dfrac{n^3}{w\sqrt{\log n}}\right)\) .
这个跑得还不算慢了,感觉也不咋难写,没事干的时候可以写一下试试 .
动态 FFT
这个好像是 EI 的成果?
其实也挺平凡的 Trick,现在应该已经普及了吧 .
动态 FFT
维护一个序列 \(\{a_n\}\)(下标显然应从 \(0\) 开始,保证 \(n\) 是 2 的整数次幂),支持:
1 x v
,令 \(a_x\gets a_x+v\) .2 x
,求 \(\operatorname{DFT}(a)_x\) .
时间分块或者叫根号重构,然后考虑 DFT 的形式:
这样每个单点加都能 \(\Theta(1)\) 算贡献,\(B\) 次暴力 FFT 重构一下 .
时间复杂度就是 \(\Theta\left(\dfrac qBn\log n+qB\right)\),取 \(B=\sqrt{n\log n}\) 得到最优时间复杂度 \(\Theta(q\sqrt{n\log n})\) .
以下是博客签名,正文无关
本文来自博客园,作者:Jijidawang,转载请注明原文链接:https://www.cnblogs.com/CDOI-24374/p/16918605.html
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0)进行许可。看完如果觉得有用请点个赞吧 QwQ