【做题记录】数据结构

二分答案,设为 \(mid\),把小于 \(mid\) 的数设为 \(0\),大于等于 \(mid\) 的数设为 \(1\)

然后就很好排序了,查询 \([l,r]\)\(1\) 的个数(即区间和),升序则把 \(0\) 填区间左边,\(1\) 填右边,降序则相反。

最后如果 \(q\) 位置的数为 \(1\),说明答案至少为 \(mid\)

因数个数 \(=\) 各质因子次数 \(+1\) 之积。

小于 \(\sqrt[3]{x}\) 的质因子直接用前缀和搞掉。大于 \(\sqrt[3]{x}\) 的质因子最多有两个,这些用 Miller-rabin + 二分处理出来,再离散化(并不需要 PR)。然后上莫队即可。

分块。设块长为 \(T\)。对每个块维护一个排好序的数组 \(b\)

修改时,整块直接打上标记。对于散块,直接加再排序并不优秀。在原数组中修改后,将 \(b\) 按是否要修改分成两队,发现它们各自仍是有序的。二路归并即可。

查询时,二分答案,查询小于答案的数的个数。对于整块,在 \(b\) 中二分。对于左右两个散块,暴力统计并不优秀。在 \(b\) 中把两个散块要查询的部分取出,再归并起来,就可以二分了。

时间复杂度 \(O(q\dfrac{n}{T}\log^2n+qT)\)\(T=\sqrt{n}\log n\) 时最优,为 \(O(q\sqrt{n}\log n)\)

二分答案。设为 \(mid\)。把小于 \(mid\) 的数设为 \(-1\),大于等于
\(mid\) 的数设为 \(1\)

如果一段区间的和 \(\ge 0\),说明其中位数至少为 \(mid\)

要判断是否存在一个子区间满足这个条件,考虑让区间和最大,即 \([b+1,c-1]\) 的和 \(+\;[a,b]\) 的最大右子段和 \(+\;[c,d]\) 的最大左子段和。

用主席树即可。

首先离散化,注意不要去重。

对于每个区间,用莫队维护 bitset 表示数的出现情况。考虑将多个相同的数分开,如果一个数 \(x\),出现了第 \(b[x]\) 次,给它一个标号 \(x+b[x]-1\),删除同理。这样就可以把相同的数字集中在值域上以 \(x\) 为左端点的区间内。由于离散化时没有去重,不同数字间互不干扰。最后用三个区间的总长,减去它们对应的 bitset 按位与的结果中 \(1\) 的个数 \(\times 3\) 即可。

小技巧:把询问分成 \(10\) 组,防止 MLE。

先考虑序列上的做法。设 \(f(x,y)\) 表示 \([1,x]\)\([1,y]\) 的答案。那么 \([l,r]\) 的答案可表示成 \(f(r,r)-f(l-1,r)-f(l,r-1)+f(l-1,l-1)\)。然后就可以莫队 / 分块了。

现在到了树上,还有换根,无非就是多了 dfs 序、分类讨论和倍增。

还是用 bitset。维护正反两个 bitset,表示每个数的出现情况(对于 \(x\),把它放在 b1\(x\) 处和 b2\(M-x\) 处,\(M\) 表示值域)。

加法:若 \(a+b=x\),则 \(a=x-b=x-(M-b^\prime)=b^\prime-(M-x)\),即 b1&(b2>>(M-x))

减法:b1&(b1>>x)

乘法:暴力枚举因数

除法:若 \(x>\sqrt M\),枚举 \(a\),检查 \(a\)\(ax\) 是否都存在;否则,预处理出 \(pos_{i,k}\),表示 \([1,i]\) 内最大的 \(j\),满足 \(a_j\times k=a_i\)\(a_i\times k=a_j\),并对每个 \(k\) 做前缀最大值。查询时判断 \(pos_{r,x}\) 是否 \(\ge l\)

发现 1 和3 操作可以分为父亲和儿子,而父亲很好处理。

对于每个节点,将其所有儿子的权值放到一个 trie 里,并从低位开始(因为要支持加 \(1\))。再维护每位上有几个 \(1\)和一个加法标记。

考虑二进制加法:找到最右边的 \(0\),把它右边的 \(1\)全部变成 \(0\),并把这个 \(0\) 变成 \(1\)。对应到 trie 树上就是:交换两个儿子,并往现在的 \(0\) 子树走(过去的 \(1\) 子树)。

单点修改其实就是在父亲的 trie 中删除再插入。

用 LCT 维护链上最小值(以边的编号为权值),即最大生成树。

加入边 \(i\) 时,若两点已联通,设两点路径上的最小编号为 \(p_i\)。对于一次询问,考虑一种使编号小的边尽可能被使用,并构成森林的策略选边。如果 \(p_i\ge l\),由于 \(p_i\) 是路径上的最小编号,路径上的所有边都会出现,边 \(i\) 也就没用了。反之,就要把 \(i\) 加入森林。所以询问其实是求 \([l,r]\)\(p_i<l\) 的数量,用主席树维护。答案就是点数 \(-\) 边数。

朴素做法:对每个数开一个 vector,按顺序存储值为这个数的下标。预处理出 \(l..r\) 块的答案,枚举散块的每个数,二分出出现次数并和整块的答案比较。

但复杂度是 \(O(n\sqrt{q\log n})\),不够优秀。

记录一个 \(t_i\) 表示 \(i\)\(a_i\) 的 vector 中的位置。

枚举散块每个位置 \(i\),设当前答案为 \(ans\),若 \(a_i\) 的 vector 中第 \(t_i+ans\) 个数仍不超过 \(r\),说明 \(a_i\) 更优。然后一直加 \(ans\) 直到 vector 中第 \(t_i+ans\) 个数超过 \(r\) 或不存在。

复杂度证明:设整块答案为 \(ans\),那么实际答案一定不超过 \(ans+2T\),也就是 \(ans\) 最多加 \(2T\) 次。

分块 + ST 表。

设块长为 \(T\),记录每块最大值,块内的前、后缀最大值,并对整块构建 ST 表。

若区间横跨多个块 \(T\),可以 \(O(1)\) 求得答案。否则可以暴力,由于数据随机,这些区间的概率为 \(\dfrac{T}{n}\)

时间复杂度 \(O(q+\dfrac{qT^2}{n}+\dfrac{n}{T}\log n)\),取 \(T=\log n\) 最优,为 \(O(n+q)\)

莫队二次离线,用值域分块平衡最后查询的复杂度。

堆优 Dijkstra + 主席树

二进制下,加上 \(2^k\),其实就是从第 \(k\) 位往高位找到第一个 \(0\),把它改为 \(1\),并把这个 \(0\)\(k\) 的所有 \(1\) 改成 \(0\)

只有两次修改,可以上主席树。

比较大小可以用哈希,先判断高位子树是否相等,相等则往低位子树走,否则往高位子树走。

有一个 弱化版

考虑在树上把 \(dep^k\) 做差分,即 \(v_u=dep_u^k-(dep_u-1)^k\)

对于 \(x,y\),把 \(1\)\(y\) 路径上所有点的点权设为该点的 \(v\),其他点点权为 \(0\)。令 \(z=LCA(x,y)\),那么 \(1\)\(x\) 路径上的点权和就是 \(1\)\(z\) 的点权和,即 \(dep_z^k\)

把询问按 \(x\) 排序,就变成了路径修改、路径求和的问题,即:

维护一个数组 \(b\),初始为 \(0\)

  1. \(1\)\(x\) 上的所有点 \(i\)\(b_i\leftarrow b_i+v_i\)

  2. 求路径上 \(b_i\) 的和。

树剖 + 线段树实现。

对于非叶子节点,设左右子树和整棵子树选到 \(x\) 的概率分别是 \(L_x,R_x,F_x\)

\(F_x=p(L_x\sum\limits_{i<x}R_i+R_x\sum\limits_{i<x}L_i)+(1-p)(L_x\sum\limits_{i>x}R_i+R_x\sum\limits_{i>x}L_i)\)

用线段树合并优化,递归时维护两个标记表示两棵树分别要乘多少。

分块。区间第 \(k\) 小考虑值域分块。

对于每个块,用并查集把值相同的下标合并(从大的下标指向小的下标),并记录每个数的根。这样我们只需要保证根是真正的值。整块修改时判断是否已经有了 \(y\),如果有了就把 \(x\)\(y\) 合并起来,并把根的值改为 \(y\)。散块暴力重构。

设区间长度为 \(L\),对于一个数 \(x\),如果它出现了 \(k\) 次,贡献为 \(x\times\) 包含 \(x\) 的子序列个数,即 \(x(2^L-2^{L-k})\)

考虑莫队 + 根号分治。预处理出出现次数 \(>\sqrt n\) 的数,每次询问暴力枚举这些数计算贡献。对于其他数,令 \(sum_i\) 为出现了 \(i\) 次的数之和(注意不要取模,每次模数会变化)。枚举出现次数,计算这些数的贡献。

用光速幂即可消去一个 \(\log\)

发现 \(3,4,5,6\) 操作其实是对连续极长 \(0,1\) 段的两端点进行修改。可以用平衡树维护这些极长段。但有可能修改产生了长度为 \(0\) 的段,可以维护最小区间长度,判断是否存在,暴力删除。

可以分开维护(标记处理较方便),也可以一起维护(推平较方便)。

分块。对于每个块,都对颜色进行一次重标号(离散化),并预处理出每块内两两颜色的最小距离、每个颜色最左 / 右的位置。

整块修改时,如果 \(y\) 没有出现,直接修改标号,距离并不会变化。如果出现了,暴力修改原数组,更新一下距离。散块暴力重构。

复杂度分析:整块修改时,如果 \(y\) 出现了,必会导致颜色数减 \(1\),最多减 \(\sqrt{n}\) 次,每次都遍历整个块,这一部分复杂度 \(O(n\sqrt{n})\)

而散块修改最多使两个块颜色增加,这一部分 \(O(q\sqrt{n})\)

posted @ 2021-06-29 16:09  EverlastingEternity  阅读(58)  评论(0编辑  收藏  举报