UOJ Logo OldDriverTree的博客

博客

[Ynoi2015]Day2题解

2019-10-28 10:48:32 By OldDriverTree

T1:いまこの時の輝きを(Version1)

Idea:hhf Solution:lxl Std:lxl Data:lxl

https://www.luogu.org/problem/P5071

一个数的约数个数即其每个质因子出现次数加上一然后乘起来

考虑对值域进行一次根号分治,这里取阈值为 $v^{1/c}$ ,大于阈值的数最多有 $c-1$ 个

我们把区间中所有大于阈值的质因子对答案的贡献用莫队维护,小于阈值的质因子的贡献每次暴力枚举质因子,然后即查其在区间中出现次数即可

总时间复杂度$O( (n+m)v^{1/c}/logv + cn \sqrt m )$,空间复杂度$O( nv^{1/c}/logv + m )$

对这题的评价:3/11

T2:どうか、忘れないで(Version1)

Idea:lxl Solution:lxl Std:lxl Data:lxl

https://www.luogu.org/problem/P5072

考虑单次询问怎么算

对于数 $x$ ,假设出现了 $y$ 次,区间长度是 $len$

则 $x$ 对答案的贡献是 $x(2^{len-y}(2^y-1))=x(2^{len}-2^{len-y})$

$2^{len-y}$ 是除了 $x$ 之外的数有这么多个不同的子序列,这些对 $x$ 的贡献没有影响

$2^y-1$ 是所有 $x$ 构成的子序列中有这么多种至少包含一个 $x$ ,有 $1$ 种不包含 $x$

由于模数不同,不能用莫队直接维护

注意到贡献分为两部分 $x2^{len}$ 与 $-x2^{len-y}$

其中第一部分非常好维护

第二部分的贡献,可以把出现次数相同的数一起维护贡献

注意到一个区间中只有 $O( \sqrt n )$ 种不同的出现次数,这是一个自然根号

所以我们可以用一个莫队均摊地维护区间可能的出现次数,从而维护区间中所有出现次数

然后为了 $O(1)$ 实现快速幂,我们可以每次$O( \sqrt n )$算出

$2^1,2^2…2^{\sqrt n} % p$

以及$2^{\sqrt n},2^{2\sqrt n}…2^{ \sqrt n \times \sqrt n} % p$

这样就可以 $O(1)$ 快速幂了

还有一种方法是将出现次数排序,从 $2^x$ 转移到 $2^y$ 的时候的复杂度是 $log(y-x)$ ,类似于finger search的感觉,可以证明这个的复杂度是正确的

总时间复杂度 $O( n \sqrt m + m \sqrt n )$,空间复杂度 $O( n + m )$

对这题的评价:4/11

T3:世界で一番幸せな女の子(Version2)

Idea:lxl Solution:ccz Std:lxl Data:lxl

https://www.luogu.org/problem/P5073

考虑用线段树维护区间的一个凸的分段函数,这个分段函数其实也就是个半平面交

定义

cur -> L()为cur节点内部的左端最大前缀和的一个分段函数

cur -> R()为cur节点内部的右端最大后缀和的一个分段函数

cur -> MID()为cur节点内部的最大子段和的一个分段函数

cur -> L(x)代表cur节点整体被加了$x$后其最大前缀和

cur -> R(x)代表cur节点整体被加了$x$后其最大后缀和

cur -> MID(x)代表cur节点整体被加了$x$后其最大子段和

我们显然有

cur -> L(x) = max( cur -> left -> L(x) , cur -> right -> L(x) + cur -> left -> sum );

cur -> R(x) = max( cur -> left -> R(x) + cur -> right -> sum , cur -> right -> R(x) );

cur -> MID(x) = max( cur -> left -> MID(x) , cur -> right -> MID(x) , cur -> left -> R(x) + cur -> right -> L(x) );

由于两个段数分别为 $a$ 和 $b$ 的分段函数取max和加起来得到的分段函数最多有 $a+b$ 段,而且仍然都是凸函数

所以我们可以递归建线段树,然后由儿子归并得到其的分段函数

这里总复杂度是$T(n)=2T(n/2)+O(n)=O( nlogn )$的

这个其实和不带修改的线段树查询区间最大子段和类似,不过我们维护的是关于这个点被加了多少的一个分段函数从而计算出当前的最大前缀,最大后缀,最大子段和

然后考虑按照全局加有负数,可以按照全局加的这个东西离线,这样就可以做到$O( nlogn + mlogn )$了

总时间复杂度 $O( (n+m)logn )$,空间复杂度 $O( nlogn )$

对这题的评价:6/11

[Ynoi2015]Day1题解

2019-10-28 09:58:08 By OldDriverTree

T1:ただいま帰りました(Version3)

Idea:lxl Solution:lxl Std:lxl Data:lxl

https://www.luogu.org/problem/P5068

考虑对每个点维护$n$个bitset,$f[i][j]$表示距离这个点小于等于j的点数

可以通过BFS来处理出这些bitset,每次查询的时候把一些bitset or起来就可以了

总时间复杂度$O( nm + { n \sum a } / w )$,空间复杂度$O( n^3/w )$,可以通过压位BFS来改进BFS的时间

对这题的评价:1/11

T2:いずれその陽は落ちるとしても(Version?)

Idea:ccz Solution:ccz Std:ccz Data:ccz

https://www.luogu.org/problem/P5069

如果一个位置 $x$ 是极大值(即 $a[x-1] < a[x] \ge a[x+1]$ ),那么 $x-1$ 和 $x+1$ 不会有机会成为最大值,且 $x$ 会成为最大值 $a[x]$ 次,此时可以把 $a[x-1]$ 和 $a[x+1]$ 直接设为 $0$ 而不影响答案

将 $a[x-1]$ 和 $a[x+1]$ 设为 $0$ 后可能会出现新的极大值,最终整个序列所有非 $0$ 位置都为极大值,非 $0$位置的值之和即为所求答案

分析以上找极大值的过程可知,实际上是从每个初始极大值开始向两侧隔一个位置取一个位置,直到遇到极小值($a[x-1] \ge a[x] < a[x+1]$)或边界($x=1$ 或 $x=n$)为止,一个极小值当且仅当在和两侧极大值距离均为偶数时会成为极大值

于是可以用数据结构维护极大值、极小值以及它们之间的区间

可以用平衡树/线段树维护极大值和极小值位置构成的序列,支持插入删除查询前驱后继,用树状数组/线段树分别维护原序列奇偶位置的前缀和,支持单点修改

也可以直接用线段树维护原序列,分类讨论两个区间信息合并的每种情况。

总时间复杂度$O( n + qlogn )$,总空间复杂度$O( n )$

对这题的评价:3/11

T3:たとえ未来が見えなくても(Version3)

Idea:lxl Solution:lxl Std:lxl Data:lxl

https://www.luogu.org/problem/P5070

设我们需要输出长 $1$ 到 $k$ 的所有极长值域连续段个数

考虑扫描线,扫右端点,维护所有左端点的答案

对每个数,显然我们只需要考虑其离右端点最近的一次出现位置,记 $x$ 最近的一次出现位置为 $pos_x$

可以发现我们扫到 $x$ 位置,其值为 $a_x$ 时,我们只需要更新其对值域在 $a_x-k$ 到 $a_x+k$ 这个范围内的数的影响

我们从 $a_x$ 这个位置开始朝两边拓展,设现在的区间是 $[l,r]$ ,则每次根据 $pos_{l-1}$ 和 $pos_{r+1}$ 中离右端点最近的一个,拓展到 $l-1$ 或者 $r+1$

不失一般性设拓展到了 $[l-1,r]$ ,相当于说如果我们左端点到当前右端点的区间包含了 $pos_{l-1}$ ,则其存在一个长为 $r-(l-1)+1$ 的非极长值域连续段

显然对于 $x$ 我们最多拓展 $k$ 次,形成了一个类似阶梯的结构,于是可以处理出每个长度的值域连续段所对应的左端点的范围

维护 $k$ 个树状数组,对每个进行一次区间加操作,总共进行 $O(k)$ 次区间加操作即可

总时间复杂度 $O( (n+m)klogn )$,空间复杂度$O( n + mk/w )$

对这题的评价:4/11

[Ynoi2014]Day2题解

2019-10-27 11:52:44 By OldDriverTree

T1:帰らぬ者と、待ち続けた者たち(Version?)

Idea:kcz Solution:kcz/lca Std:kcz/lca Data:kcz

https://www.luogu.org/problem/P5065

将原序列分成k块,每个块维护块内每个长度下or的最大值

修改时,块内暴力重新计算每个长度下or的最大值,就是枚举右端点,然后到他的or和不同的左端点只有log种,枚举就可以了 时间复杂度$O(n/k \times log(n))$

询问时,答案在块内的情况: check每个块,如果可以把答案减少$1$,就更新答案,然后重复操作,否则退出 时间复杂度$O(k+n/k)$

如果答案跨过块,则从左到右枚举块,同时维护块左边所有不同的后缀or和 具体就是从一个块到下一个块的时候,把所有不同的后缀or和都or上自己整个块的or和,然后再加入自己所有不同的后缀or和(提前维护好)。之后去一下重 时间复杂度$O(k \times log(n))$

现在考虑答案右端点在块内,左端点在块左边的情况,然后从左到右枚举块内所有不同前缀or和(提前维护好) 如果他跟 当前最大的在块前面的后缀or和 的or和大于等于$k$,就更新答案并删除当前最大的在块前面的后缀or和,然后重复操作 否则退出。 时间$O(k \times log(n))$

$k$取$\sqrt n$时,

总时间复杂度$O(m sqrt(n) log(n))$

用一个线段树维护每个节点每种前缀 or 和以及每种后缀 or 和,然后暴力算出这两者一一对应的最优 or 和

再用一组堆维护每种长度上述所有的 or 和,再用一棵线段树维护每种长度 or 和的最小代价。查询时二分即可。

时间复杂度$O( mlog^2nlog^2a )$,其中修改一次的复杂度为$O( log^2nlog^2a )$,查询一次的复杂度为$O( logn )$,但是感觉平衡只能去掉$polyloglog(n)$,难以去掉log

对这题的评价:3/11

T2:誰も彼もが、正義の名のもとに(Version5)

Idea:lxl Solution:lxl Std:lxl Data:lxl

https://www.luogu.org/problem/P5066

考虑用平衡树维护序列,将一段连续的同色极长段缩为一个点

发现操作具有对称性,本质上只有

区间染色为0/1

区间每个数or/and上左/右

区间和

这三种操作

发现第二种操作其实就是让区间中0/1的同色极长段各自向左/右扩散一格的位置

我们修改的时候不修改这个给定的区间,而是让区间两端点根据操作种类和所在的同色极长段的颜色进行调整,具体来说就是检查一下是否包含这个同色极长段,然后调节我们这次修改的区间的位置

然后问题便转化为,每次区间中0/1的同色极长段各自变大1或者缩小1,这个可以通过在缩点平衡树结构上打标记来实现

因为有可能一个同色极长段在缩了之后变成0,所以我们需要维护一下区间内最小的同色极长段长度,如果是0则暴力将其删去,同时合并两边的段

可以发现每次修改只会增加$O(1)$个同色极长段,每次删除可以花$O( logn )$的代价减少一个同色极长段

总时间复杂度$O( (n+m)logn )$,空间复杂度$O( n )$

对这题的评价:4/11

T3:消えない過去、消えていく未来(Version?)

Idea:mcfx Solution:mcfx Std:mcfx Data:mcfx

https://www.luogu.org/problem/P5067

题解 为了描述简洁,以下均用 $K$ 指代括号层数。

由于长度和操作次数同阶,以下均用 $N$ 指代这两个量。

算法 1

考虑用一棵树存储整个串。

查询时类似线段树或平衡树的区间查询。

对于第一个部分分,这棵树需要有 + - * 和数字几种节点。

+ 和 - 节点由于运算优先级相同,可以一起处理。

为了满足运算优先级的要求,需要数字节点的深度 > * 节点的深度 > + - 节点的深度。

显然,这样一棵树是非常容易构建的,可以直接当成非旋转 Treap,在合并操作时判断一下优先级即可。

时间复杂度:$O(N log N)$。

空间复杂度:$O(N)$。

期望得分:10 分。

算法 2

加上括号后,需要一种新的节点表示括号。

这里我们定义一种新节点 () ,表示一对括号。每个 () 节点有三个儿子,左儿子、右儿子和中儿子,左儿子和右儿子的含义与其他节点相同,中儿子表示括号内的表达式。 () 节点和其他节点合并时,优先级与数字节点相同。 对于不成对的括号,可以在原串最左边添加若干 ( ,最右边添加若干 ) 配成对,添加的括号显然是 $O(N)$ 的。

现在问题是如何构建一棵满足条件的树。

对于一个括号合法的表达式,可以从左往右扫一遍,用栈维护当前每层括号内的表达式。

当一对括号闭合时,添加一个 () 节点,中儿子指向内部的根,再与上一层合并即可。

查询时需要特判区间只包含一边括号的情况。

时间复杂度:$O(N log N ⋅ K)$。

空间复杂度:$O(N)$。

期望得分:20 分。

算法 3

考虑没有括号,但是有修改操作的情况。

当修改后的运算优先级不变时,可以直接修改。

如果修改后的运算优先级发生改变,可以先把该节点和左右的表达式分裂,修改完再合并到一起,这个可以用非旋转 Treap 很容易的实现。

时间复杂度:$O(N log N)$。

空间复杂度:$O(N)$。

期望得分:30 分,与算法 2 结合可得 40 分。

算法 4

如果同时有括号和修改,可以沿用算法 2 中树的结构。

对于修改操作,如果被修改的不是括号,可以与算法 3 类似的处理。不过这里只需要和最内层括号内的表达式分裂、合并。

如果修改了括号,可以把每个操作分解为若干个下列操作:

1.加入一个括号;

2.删除一对括号;

3.加入或删除一个其他节点。

第三种操作可以直接在 Treap 上处理。

第二种操作只需要把左节点、中节点、右节点依次合并即可。

对于第一种操作,多了一个不成对的括号,可以视为在无穷远处加了一个方向相反的括号。

如图,左边表示操作前树的形态,右边表示操作后的。圆点表示 () 节点,方点表示其他节点,圆点下方连接的节点表示括号内的内容。蓝色括号表示实际的括号位置,红色括号表示新加入的括号。

加入这两个括号后,需要新加入一个 () 节点,然后把每层括号的一侧分裂,然后合并到上一层。这个过程可以从上到下递归实现。

时间复杂度:$O(N log N ⋅ K)$。

空间复杂度:$O(N)$。

期望得分:100 分

可以利用splay来将时间复杂度改进到$O( N ( K + log N ) )$。

对这题的评价:5/11

[Ynoi2014]Day1题解

2019-10-19 02:35:57 By OldDriverTree

T1:太陽の傾いたこの世界で(Version1)

Idea:ccz Solution:ccz Std:ccz Data:ccz

https://www.luogu.org/problem/P5062

维护一棵红黑树(题意指的是对应2-3-4树的红黑树),可以发现每次插入后至多发生两次旋转。

旋转发生的情况是:一个黑点的孩子为一红一黑,但红色孩子有一个孩子为红色,此时视情况单旋或双旋。

不旋转而只调整颜色的情况是:一个黑点的左右孩子都是红色,且左右孩子中有一个有红色的孩子,此时将黑点的孩子设为黑色,黑点设为红色,继续考虑是否有两个红点是父子。

如果一个点的左或右孩子为空,那么有一个区间内的数插入后会在这个空位上,同时每个数也对应了一个空位,于是可以从空位开始,自底向上利用红黑树的结构和性质计算旋转次数。

对每个黑点,记录有几个数插入到子树中可以使黑点变红;

对每个红点,分别记录左右孩子变红的方案数(考虑左右孩子为黑点和为空的情况);

对每个点,记录子树中插入一个数,在子树内造成的旋转次数;

维护了这些信息,可以在插入时在根维护全局的答案。

查询的是分别插入$[1,x]$内的整数造成的旋转次数,只要找出x对应的空位,将这个空位到根的路径的答案重新计算,不考虑大于x的数的贡献,就得到了答案。

总时间复杂度$O( mlogn )$,空间复杂度$O( n + m )$

对这题的评价:3/11

T2:空の上の森の中の(Version1)

Idea:zcy Solution:lxl Std:lxl Data:lxl

https://www.luogu.org/problem/P5063

可以证明这个线段树只有$O( logn )$种不同大小的节点

对于每种节点大小分层,维护一个数据结构,支持:

1.区间加

2.单点修改

3.区间rank

这个是一个经典问题,目前离线上界是$O( m \sqrt n )$,在线上界是$O( m \sqrt {nloglogn} )$,我们通过一些规约证明了这个问题不弱于一个$\sqrt n \times \sqrt n$的矩阵乘法,所以目前认为难以达到$O( n \times polylogn )$的复杂度

可以证明线段树的每层只有$O(1)$种不同大小的节点,那么我们的总时间复杂度是

$O( m \sqrt n + m \sqrt {n/2} + m \sqrt {n/4} + ... )$ = $O( m \sqrt n )$

空间复杂度可以通过一些trick做到$O( n + m )$

对这题的评价:3/11

T3:この戦いが終わったら(Version3)

Idea:lxl Solution:shadowice1984 Std:shadowice1984 Data:lxl

https://www.luogu.org/problem/P5064

考虑值域分块,查询kth的时候先在值域分块上面找到kth属于哪个块,然后枚举块内元素即可

枚举块内元素时,即查询一个值是否在一个时刻属于一个连通块,在这个题里面由于支持撤销,所以需要使用时间复杂度$O( logn )$的按秩合并并查集,这部分是平凡的

然后考虑如何找到kth属于哪个块,一个简单粗暴的思路是并查集上每个点维护一个长度为$O( \sqrt {nlogn} )$的数组,即值域分块的数组,然后合并两个点的时候暴力把两个数组相加,这样我们就可以求kth了

这样空间复杂度是$O( n \sqrt {nlogn} )$的,会MLE,考虑如何优化空间复杂度

如果我们使用按size合并的并查集的话,可以发现这个并查集的树高是$O( logn )$级别的

可以发现我们每个连通块如果开一个数组来维护值域分块结构很浪费,因为对于小的连通块这个分块结构是非常不满的,而大的连通块个数很少

所以如果我们把数组换成一个有序的链表,即用链表来维护这个值域分块,此时我们的空间复杂度将会骤降至$O( nlogn )$

具体点来讲我们在并查集上每个节点维护一个链表,即启发式合并这个值域分块的链表,这里链表中的节点上存一个二元组$(id,cnt)$表示这个集合中有cnt个属于编号为id的值域块的点,查kth的时候for一遍这个有序的链表就可以了

如此这般分析的话我们会发现一个节点的链表size为$\min(size(p),\sqrt n))$

简单分析一下空间复杂度就是$O( nlogn )$的,因为每一个点最坏情况下会在每个祖先处被存储一次

然后考虑一下完全可持久化带来的影响

先将所有操作的版本树建出来,然后我们可以通过在版本树上面DFS来实现题目中的所有操作

每次撤销一个合并操作的时候,可以通过将两个值域分块的链表相减来实现,这个的时间复杂度还是$O( \sqrt {nlogn} )$的

总时间复杂度$O( m \sqrt { nlogn } )$,空间复杂度$O( nlogn + m )$

考虑怎么去掉log,并查集的这个log可以对原树进行分块之后加一些trick来去掉,空间的这个log可以通过逐块处理来去掉

总时间复杂度$O( m \sqrt n )$,空间复杂度$O( n + m )$

对这题的评价:4/11

[Ynoi2018]Day2题解

2019-09-16 12:24:16 By OldDriverTree

我信概最后写了个什么"从序列维护到动态图的数据结构概述"然后只拿了3.0,果然是傻逼课,建议大家转专业的话别选这个课。

T1:そらのおとしもの(Version2)

Idea:lxl Solution:lxl Std:lxl Data:lxl

https://www.luogu.org/problem/P5397

http://www.51nod.com/Challenge/Problem.html#problemId=2046

"弑尽破净的第四分块" (妈呀这名字好中二啊(谁叫我要用日本轻小说中的东西命名真是作死))

这题就是我想改一个已有的经典根号形式改出来,然后想了想发现可以做

考虑根号分治

定义一个值x出现次数为size[x]

如果没有修改操作,则预处理所有size大于$ \sqrt n$的值到所有其他值的答案,以及每个值出现位置的一个排序后的数组

查询的时候如果x和y中有一个是size大于$ \sqrt n$的,则可以直接查询预处理的信息,否则进行归并

$O( n \sqrt n + m \sqrt n )$

有修改操作即在这个做法上改良,因为发现这个做法的根号平衡并没有卡满,所以有改良余地

假设把所有x变成y

由于可以用一些技巧使得x变成y等价于y变成x,所以不妨设size[x] < size[y]

定义size大于$ \sqrt n$为大,size小于等于$ \sqrt n$为小

定义ans[x][y]为x到y去除附属集合的部分的答案(附属集合是什么下面有说)

x取遍所有值,y只有所有大的值,总$O( \sqrt n )$个

修改:

如果x和y均为大,则可以暴力重构,$O( n )$处理出y这个值对每个其他值的答案,因为这样的重构最多进行$O( \sqrt n )$次,所以这部分复杂度为$O( n \sqrt n )$

如果x和y均为小,则可以直接归并两个值的位置的排序后的数组,单次$O( \sqrt n )$,所以这部分复杂度为$O( m \sqrt n )$

如果x为小,y为大,发现小合并进大的均摊位置数为$O( n )$,对这个再次进行根号平衡

对于每个大,维护一个附属的位置集合,这个位置集合的大小不超过$O( \sqrt n )$

每次把小的x合并入大的y的时候,即合并入这个附属集合,并且用x到所有大的值的答案更新y到所有大的值的答案,这样

如果合并后附属集合大小超过$O( \sqrt n )$,则$O( n )$重构y到所有值的答案,这个过程最多进行$O( \sqrt n )$次,均摊复杂度$O( n \sqrt n )$

由于大的值个数$ \le \sqrt n$ , 附属集合大小$ \le \sqrt n$,这一部分是单次$O( \sqrt n )$的,所以这部分复杂度为$O( m \sqrt n )$

查询:

如果x和y均为大,则在维护的答案中查询min( ans[x][y] , ans[y][x] ),然后将x的附属集合和y的附属集合进行归并,这一部分是单次$O( \sqrt n )$的,所以这部分复杂度为$O( m \sqrt n )$

如果x和y均为小,则进行一次归并即可,所以这部分复杂度为$O( m \sqrt n )$

如果x为小,y为大,则在维护的答案中查询ans[x][y],然后将x和y的附属集合进行归并,这一部分是单次$O( \sqrt n )$的,所以这部分复杂度为$O( m \sqrt n )$

由于维护了所有可能的贡献,而且更新是取min,正确性也得到了保障

时间复杂度$O( (n+m) \sqrt n )$,空间复杂度$O( n \sqrt n )$

存在序列分块做法

对这题的评价:6/11

T2:ゴシック(Version2)

Idea:lxl Solution:lxl Std:lxl Data:lxl

https://www.luogu.org/problem/P5398

https://cometoj.com/problem/1072

"点缀光辉的第十四分块" (妈呀这名字好中二啊(谁叫我要用日本轻小说中的东西命名真是作死))

这题是我研究区间逆序对之后,枚举了一些区间pair贡献的题里面的一个

最开始枚举出来的时候我只会$O( n^{1.5+\epsilon } )$的暴力做法(实际上在n=1e5的范围内接近于$O( n^{7/4} )$),然后过了一年再看到这题就会了......

我们考虑莫队维护,不失一般性这里只讨论莫队的四种拓展方向里面的一种

每次把区间$[l,r]$拓展到$[l,r+1]$的时候需要维护$a[r+1]$和$[l,r]$这段区间的贡献

这里利⽤莫队二次离线的trick:

莫队就可以抽象为&n \sqrtm&次转移,每次查询一个序列中的位置对一个区间的贡献

这个贡献是可以差分的, 也就是说把每次转移的$a[r+1]$对区间$[l,r]$的贡献差分为$a[r+1]$对前缀$[1,r]$的贡献减去对前缀$[1,l-1]$的贡献

然后通过这个差分我们可以发现,我们把$O( n \sqrt m )$次的修改降低到了$O( n )$次修改,因为前缀只会拓展$O( n )$次

于是我们每次可以较高复杂度拓展前缀和,因为插入次数变成了$O( n )$次

然后这⾥我们离线莫队的转移的时候可以做到线性空间,因为每次莫队是从一个区间转移到另一个区间:

我们记下$pre[i]$为$i$的前缀区间的答案,$suf[i]$为$i$的后缀区间的答案

不失一般性,只考虑把区间$[l1,r1]$拓展到区间$[l1,r2]$:

$[l1,r1]$区间的答案为红色部分的贡献,$[l1,r2]$区间的答案为红色部分加上绿色部分的贡献,$r2$的前缀贡献为所有的贡献,$r1$的前缀贡献为红色的贡献与前两个蓝色的贡献

于是我们可以把绿色的贡献,也就是两次询问的差量差分为$pre[r2]-pre[r1]-${$[1,l1-1]$对$[r1+1,r2]$的贡献}

然后我们再差分⼀下$[1,l1-1]$对$[r1+1,r2]$的贡献为$[1,l1-1]$对$[1,r2]$的贡献减去$[1,l1-1]$对$[1,r1]$的贡献,这里每个位置开个vector存⼀下就可以了

于是我们达成了$O( n + m )$的空间复杂度将莫队给⼆次离线

现在考虑怎么算这个贡献,我们要高效计算区间$[l,r]$对$x$的贡献时

先把贡献拆开变成:$[l,r]$中$x$的倍数个数,$[l,r]$中$x$的约数个数

$[l,r]$中$x$的倍数个数:每次前缀加入一个数$a_i$的时候,我们把$a_i$的约数位置所对应的数组$pre1$的位置都++

然后查询的时候直接查$pre1[x]$即可得到$x$倍数个数

$[l,r]$中$x$的约数个数:这里考虑对$x$的约数进行一次根号分治

对于所有$x$⼤于$\sqrt n$的约数,我们拓展前缀的时候维护,即每次把$a_i$的倍数位置所对应的数组$pre2$的位置都++

然后查询的时候直接查$pre2[x]$即可得到$x$大于$\sqrt n$的约数个数

对于所有$x$小于$\sqrt n$的约数,我们考虑每次直接枚举区间$[l,r]$中$1,2,...\sqrt n$的出现次数

这里可以先枚举$1,2,...\sqrt n$,然后预处理一个前缀和,$O( n + m )$算出对每个询问的贡献,从而节省空间

于是我们得到了⼀个总时间复杂度$O( n( \sqrt n + \sqrt m ) )$,空间$O( n + m )$的做法

对这题的评价:8/11

T3:駄作(Version1)

Idea:lxl Solution:ccz Std:ccz Data:ccz

https://www.luogu.org/problem/P5399

https://loj.ac/problem/6612

"????的第七分块" (不中二了)

这个是我在军训走路的时候想出来的东西,然后我不会做,但是ccz会做

最开始给的做法是在静态top tree上跑莫队二次离线什么的,后面发现存在直接分块而不利用分治的做法,所以这题变简单了很多

这里只给出树分块做法,并不给出树分治做法

对于树上的顶点 $a,b$,顶点集 $S,S1,S2$,定义 $d(a,b)$表示顶点 $a$ 到 $b$ 的距离,若 $a=b$ 则 $d(a,b)=0$,否则 $d(a,b)$是 $a,b$ 间简单路径的边数; 另外定义 $d(S,a) = d(a,S) = ∑ d(a,b)[b∈S]$ $d(S1,S2) = ∑ d(a,S2)[a∈S1]$ 。

树 $T$ 上的一个邻域 $NT(x,y)$定义为到顶点 $x$ 距离不超过 $y$ 条边的顶点集。$x$ 称为邻域的中心, $y$ 称为邻域的半径。

真实的树分块——Topology Cluster Partition(名字正确性存疑)

树分块将 n 个结点的树分成了一些块的并,满足下列性质:

1.每个块是树

2.每个块中有两个特殊的点,称为端点 1 和端点 2。

3.不同块的边集不相交

4.一个块中的顶点,除端点外,其余顶点不在其它块中出现

5.如果一个顶点在多个块中出现,那么它一定是某一个块的端点 2,同时是其余包含这个顶点的块的端点 1

6.如果把所有块的端点作为点,每块的端点 1 和端点 2 连有向边,则得到一棵有根树

这里给出树分块的一个实现,满足块数和每块大小均为 $O( \sqrt n )$:

在有根树中,每次选一个顶点 $x$,它的子树大小超过$\sqrt n$,但每个孩子的子树大小不超过$\sqrt n$, 把 $x$ 的孩子分成尽可能少的块(以 $x$ 为端点 1,但暂时允许有多个端点 $2$,且每块至多有 $2 \sqrt n$ 个顶点),然后删掉 $x$ 的子树中除 $x$ 外的部分。重复直到剩下的点数不超过$\sqrt n$,自成一块。 这样就求出一个保证块中顶点数和块数均为 $O( \sqrt n )$的树分块(块间可以共用一些顶点,但每条边只属于一个块),如果一个块有多个端点(即被多个块共用的点),则在块内将这些端点建出虚树,将虚树上的每条边细分为一个新的块,以保证最终每个块只有两个端点。

如果只有单次询问,可以进行转化: $d(S1,S2) = ∑[d(a,c)*|S2|+d(b,c)*|S1|−2d(lca(a,b),c)][a∈S1,b∈S2]$,其中 $c$ 是任意一个点。这里我们取$c$为根节点,这样把两两顶点间的距离和转化为了点集内顶点的深度和以及两两顶点间 lca 的深度和: $d(S1,S2) = ∑[dep(a)*|S2|+dep(b)*|S1|−2dep(lca(a,b))][a∈S1,b∈S2]$ 对 lca 部分可以在$ O(n)$时间内求出:初始化树的边权为 $0$,将 $S1$ 中的每个顶点到根的路径的边权$+1$,求 $S2$中每个顶点到根的路径的边权和之和。

首先进行树分块,方便起见,认为一个块包含块中除了端点 $1$ 之外的点,这样每个点就恰好属于一个块。 预处理每个块 $B$(端点为 $a1,a2$)中的 $d(NB(ai,x),aj)(i,j=1,2)$,需要 $O(n)$时间和空间。

树 $T$ 的一个邻域可以拆分为这个邻域和每个块的交。

对于块 $B$,若 $x$ 在 $B$ 中,则 $NT(x,y)∩B=NB(x,y)$; 否则设 $z$ 是 $B$ 中距离 $x$ 较近的端点,距离为 $d$,则 $NT(x,y)∩B=NB(z,y-d)$。

求两个邻域间的距离和,只需考虑这两个邻域和每个块的交。求块 $B1,B2$ 的两个邻域 $n1=NB1(x1,y1)$和 $n2=NB2(x2,y2)$间的距离和。 如果 $xi$ 不是 $Bi$ 的端点,则显式地求出 $ni$ 以及 $ni$ 到 $Bi$ 的端点的距离和,这需要 $O( \sqrt n )$的时间。

当 $B1≠B2$ 时,设 $a$ 为 $B1$ 中最靠近 $B2$ 的端点,$b$ 为 $B2$ 中最靠近 $B1$ 的端点,$n1$ 和 $n2$ 间的距离和 $d(n1,n2)=d(a,b)*|n1|*|n2|+d(n1,a)*|n2|+d(n2,b)*|n1|$。通过在块构成的树上 dfs,这部分可以在 $O( \sqrt n )$时间内处理。

当 $B1=B2$ 时,如果 $x1$ 和 $x2$ 都是 $B1$ 的端点,每块每次询问这种情况出现至多 $1$ 次,这里暂不考虑,最后再离线处理;否则每次询问这种情况只出现 $O(1)$次,可以 $O( \sqrt n )$暴力处理。

离线处理的部分,枚举每个块 $B$,再枚举询问(只需考虑两个邻域的中心都不在 $B$ 中的询问) 计算贡献,有 $O(n)$个询问形如 $d(NB(a,y1),NB(b,y2))$,对<$a,b$>的 $4$ 种情况分别计算。由于块中 点数的限制,<$y1,y2$>实际只有 $O(n)$种($y1,y2=O( \sqrt n )$)。 仍然转化为求两两点间 lca 的深度和,枚举 $y1$,维护初始边权为 $0$,将 $NB(a,y1)$中每个点到 根路径上的边权$+1$ 后得到的树,此时每个 $y2$ 对应的答案是 $NB(b,y2)$中每个点到根的路径的边权和之和。这部分时间和空间都是 $O(n)$的。

时间复杂度$O( (n + m)\sqrt n )$,空间复杂度$O( n+m )$

最后推荐一下这个gal,挺好玩的

对这题的评价:10/11

OldDriverTree Avatar