刷题系列——数据结构

方差

D D 为方差, S S 为区间和, S 2 S_2 为区间平方和。

D = 1 r l + 1 i = 1 r ( a i S r l + 1 ) 2 = 1 r l + 1 i = 1 r ( a i 2 2 S a i r l + 1 + S 2 ( r l + 1 ) 2 ) = 1 r l + 1 ( S 2 S 2 ) \begin{aligned} D &=\frac{1}{r-l+1} \sum_{i=1}^{r}\left(a_{i}-\frac{S}{r-l+1}\right)^{2} \\ &=\frac{1}{r-l+1} \sum_{i=1}^{r}\left(a_{i}^{2}-\frac{2 S a_{i}}{r-l+1}+\frac{S^{2}}{(r-l+1)^{2}}\right) \\ &=\frac{1}{r-l+1}\left(S_{2}-S^{2}\right) \end{aligned}

对区间平方和进行区间加

S 2 = i = l r ( a i + v ) 2 = i = l r ( a i 2 + 2 v a i + v 2 ) = S 2 + 2 v S + ( r l + 1 ) v 2 \begin{aligned} S_{2}^{\prime} &=\sum_{i=l}^{r}\left(a_{i}+v\right)^{2} \\ &=\sum_{i=l}^{r}\left(a_{i}^{2}+2 v a_{i}+v^{2}\right) \\ &=S_{2}+2 v S+(r-l+1) v^{2} \end{aligned}

等差序列

对于 l r l \sim r 加上一个等差序列,令首项为 a a ,公差为 D D 。可以发现等差序列公式和差分有点像,所以先进行差分,那么区间修改相当于在 l l 加上 a a ,在 ( l , r ] (l,r] 加上 D D ,在 r + 1 r+1 加上 a D × ( r l ) -a-D \times (r - l) 即可。那么查询操作是区间求和。

最长单调序列 逆序对

以最长不下降序列为例,维护一个权值线段树或树状数组,区间 [ l , r ] [l,r] a i [ l , r ] a_i \in [l,r] 的最长不下降子序列。正序扫,对每个数对桶进行区间最大值转移,再把这个数加进去更新。

逆序对同理,倒着扫,只不过区间最大值改成区间求和。

最大子段和

维护一个线段树,对于一个点表示的 [ l , r ] [l,r] ,有三类。一是左儿子的答案,二是右儿子的答案,三是左儿子的后缀与右儿子的前缀拼接。那么线段树要维护的是前缀,后缀还有区间和即可。

对于查询函数,如果当前区间被查询区间包括,返回当前区间答案。如果查询区间全部在当前节点的左儿子或右儿子内,直接递归左儿子或右儿子。否则是当前区间包括了查询区间,那么递归下去把左儿子和右儿子中的询问区间找出来,再讨论一下三类取最值。

P2824 排序

给定一个排列和若干个修改,每次修改将一段区间升序或降序排序。最后求 p p 上的数字。

这真女少口阿。

用线段树来排序是不可能的,但可以用区间求和与区间修改模拟 01 序列的排序。而且只有最后的一次查询,那么 p p 上的数字有没有单调性?先二分这个要查询的数试一试,让大于等于这个二分值的数为 1 1 ,小于的为 0 0 ,那么对每个询问对 01 序列的排序,如果最后位置 p p 1 1 那么就减小二分值,为 0 0 则增大二分值,这当然有单调性了,就做完了,带了两个 log \log

P1712 区间

在一个数轴上有 n n 个闭区间,选出 m m 个区间使得它们包含至少同一个位置。求所有合法方案的最小区间长度极差。

哎,排序区间的长度好像没什么影响,那先排序。排序后好像可以尺取法,那么就一直选,选到有 m \ge m 个公共点时就停下来,再减少长度小的区间,直到减少了这个区间就 < m <m 个公共点再停下来。那么选择的区间就是答案。

至于有多少个公共点,区间加求最大值即可。

P2633 Count on a tree

求一条树上给定的若干个路径上的第 k k 小点权。

主席树 + 树链剖分

考虑一条链上的问题,每棵主席树都是一个的前缀中每个点权出现次数的桶,那么用前缀和的思想可以求出 [ l , r ] [l,r] 每个数中出现的次数,再线段树二分一下即可。那么放到树上,可以将前缀和变成树上前缀和,用 lca 转一下就行。

HDU 6315 Naive Operations

给定一个排列 b b ,你需要维护一个初始全 0 0 的序列 a a ,支持 如下两种操作

  • 区间加 1 1
  • 查询区间 a i b i \left\lfloor \frac{a_{i}}{b_{i}} \right\rfloor 之和

直接求不方便,不然把给 a i + 1 a_i + 1 换成给 b i 1 b_i - 1 ,如果减到了 0 0 就加一 ,再把把 b i b_i 赋值回来。可以维护一个区间最小值和区间和,最小值如果减成了 0 0 就暴力递归到叶子修改,因为 b b 是排列,所以这个暴力的复杂度是调和级数不会超时。

二维数点

给定平面上 n n 个点 ( x i , y i ) , \left(x_{i}, y_{i}\right), 每次问你一个矩形内有多少点。 1 n 1 0 5 1 \leq n \leq 10^{5} 允许离线。

用二维前缀和拆成四部分去维护,这是一个二维偏序问题,先对第一维排序,用线段树或树状数组去维护即可。

蚯蚓

给你一堆数,有 n n 个,并对他们操作 m m 次。每次取出最大的一个数 x x ,称之为母数。并将 x x 分割成左端数 [ x × p ] [x \times p] 和右端数 x [ p × x ] x-[p \times x] ,并把这两个数放回数堆中,其余数均增加 q q p p 固定为 ( 0 , 1 ) (0,1) 的有理数。要求输出每次操作的数x和m次操作后所有数的和。

对于 85 % 85\% 的数据,是可以用优先队列搞的。我们用优先队列维护所有蚯蚓的长度,每次取出长度最长的蚯蚓,将其切成两半并丢回队列。有一个问题是如何让其他蚯蚓的长度增加 q q 呢?可以记录蚯蚓整体增加的长度,对被切成两半的蚯蚓,将其长度 q -q 即可。

对于 100 % 100\% 的数据,数据范围不允许带 l o g log ,但是找规律可以发现一个隐藏的单调性先切成两半的蚯蚓的一半,一定比后切成两半的蚯蚓对应的一半长。所以可以用三个队列分别维护没有被切的蚯蚓,被切成 [ x × p ] [x \times p] 的蚯蚓,和被切成 x [ p × x ] x- [p \times x] 的蚯蚓,每次选出三个队列中最大的蚯蚓切一下再丢回对应队列中即可。

永恒的契约

食物链

动物王国中有三类动物 A , B , C A,B,C A A B B B B C C C C A A 。现有 N N 个动物,以 1 N 1 \sim N 编号。每个动物都是 A , B , C A,B,C 中的一种,但是我们并不知道它到底是哪一种。

一个人按顺序说了 K K 句话,第一种说法是 1 X Y,表示 X X Y Y 是同类。第二种说法是 2 X Y,表示 X X Y Y 。但这 K K 句话真假不明,当一句话满足下列任一条件,这句话就是假话,否则就是真话。

  1. 当前的话与前面的某些真的话冲突,就是假话;
  2. 当前的话中 X X Y Y N N 大,或当前的话表示X吃X,就是假话。

求假话的总数。

并查集能维护连通性、传递性。比如朋友的朋友是朋友,朋友的敌人是敌人。但是,维护敌人的敌人是朋友就很难维护了,所以就有种类并查集的诞生。这道题有三个物种,于是我们给并查集开三倍的空间。对于每种生物,第一倍空间储存同类,第二倍储存猎物,第三倍储存天敌,我们不能确定每种生物是 A A 还是 B B 还是 C C ,知道生物的相对关系就够了。

讨论第一种话, X X Y Y 是同类,可以转换成 X X 不吃 Y Y Y Y 不吃 X X 。如果有一点不满足,那么就是谎言。如果都满足,那么 X X 的同类都是 Y Y 的同类, X X 的天敌都是 Y Y 的天敌, X X 的猎物都是 Y Y 的猎物。再讨论第二种话, X X Y Y ,可以转换成 X X Y Y 不是同类且 Y Y 不吃 X X 。如果有一点不满足,那么就是谎言。如皋港都满足,那么 X X 的同类是 Y Y 的天敌, X X 的天敌是 Y Y 的猎物, X X 的猎物是 Y Y 的同类。

Kinoman

有一个长度为 n n 的序列 f , f i 1 m f,f_i \in 1 \sim m ,有一个长度为 m m 的价值序列 w w 。选择一个区间 [ l , r ] [l,r] 获得的价值是
i = l r w f i × [ c o u n t ( f i ) = 1 ] \sum_{i=l}^r w_{f_i} \times [count(f_i)=1]
问价值最大的区间的价值。

预处理出每个数在序列 f f 中第一次的位置,和 f i f_i f f 中下一个出现的位置 n x t nxt 。用线段树维护每个位置作为右端点的答案。首先预处理出以 1 1 为左端点的答案。当 l + 1 l + 1 时, [ l , n x t i 1 ] [l, nxt_i - 1] 间答案 w f l -w_{f_l} 。而 [ n x t i , n x t n x t i 1 ] [nxt_i, nxt_{nxt_i} - 1] 间答案 + w f i +w_{f_i} 。如果 n x t nxt 不存在,可以看作是 n + 1 n + 1 。然后每次更新最大值。

练习题

给定一棵 N N 个节点的树, 每个点 i i 有权值 a i a_i 。有 Q Q 个询问, 对于询问 x , y , k x,y,k , 分别输出树上从 x x y y 的路径中, 权值小于 / 等于 / 大于 k k 的点的数目。强制在线。

这是对树的查询,我们可以通过求 l c a lca 转换成对一条链的查询。套路地,对于一个点 x x ,我们维护一个权值线段树以维护一个桶 b b ,对于每一个在根到 x x 路径上的点 y y ,令 b a y + + b_{a_y}++ 。当然,我们不能对每一个点重新建立一棵线段树,这个线段树要支持单点修改和区间求和,区间求和求的是前缀和。我们可以先 dfs 一遍原来的树,在 dfs 的过程中建立主席树。

Mex

有一个长度为 n n 的数组 a 1 n a_{1 \sim n} m m 次询问,每次询问一个区间内最小没有出现过的自然数。即求区间 mex。强制在线。

考虑建立权值线段树,维护每一个权值在原数组中最后一次出现的下标。对于查询操作 [ l , r ] [l,r] ,可以取出右端点所对应的主席树,并在树上二分查找下标小于 l l 的最小权值即为答案。虽然 a i a_i 非常大,显然答案不会超过 n n ,所以不用离散化。

猜你喜欢

转载自blog.csdn.net/qq_39984146/article/details/107534104