小清新 ds

线段树

我也不知道该分到哪里

GSS3 - Can you answer these queries III

经典最大子段和

维护前缀 max\max ,后缀 max\max ,和区间最大子段,区间和就好了

code

#2055. 「TJOI / HEOI2016」排序

首先有一个经典做法,就是二分答案,将大于 ansans 的值设为 11 ,小于 ansans 设为 00 ,这样排序就很好做了,时间复杂度 O(nlog2n)O(n\log^2 n)

但是其实还有 O(nlogn)O(n\log n) 的做法,就是你那权值线段树维护序列的连续有序段,每次操作相当于合并一些线段树,倒序就打一个 tagtag 就好了,注意边界可能在一颗线段树里面,所以还需要线段树分裂,这样复杂度就是 O(nlogn)O(n\log n) 了,因为你发现你一次操作最多分裂出来两颗线段树,所以合并是 O(n+m)O(n+m)

代码写一半丢了所以开摆了

CF1783G Weighed Tree Radius

首先将权值 aia_i 转变成向外挂一个点,边权为 aia_i (注意这里要挂两个,因为 wv(v)w_v(v) 是合法的

发现这个半径好像很不好做,再根据他的名字联想,我们可以想到求出直径,最后输出 len2\lceil \frac{len}{2} \rceil ,至于为什么这样是对的,因为离每个点最远的点一定都是直径上的点,所以中点是半径最小的

现在问题就好做了,边权修改,直径查询,用线段树维护即可,具体来说,就是根据直径性质,两颗树合并到一块新的直径端点一定出自原来两棵树的四个直径端点中,可以合并

因为常数巨大,所以我写了 O(1) LCAO(1)\ LCA 但是 strange757\text{strange757} 实测 O(nlog2n)O(n\log^2n) 可过

code (u1s1,我不知道我写的是什么

CF1192B Dynamic Diameter

线段树维护直径板子

线段树维护 dfndfn 序,每个点到根节点的权值用 bitbit 维护,修改 (u,v)(u,v) 时重新算一下所有包含 uu 的和包含 u+szu1u+sz_u-1 的就行了

code

CF1149C Tree Generator™

(( 表示往下做一步,所以深度 +1+1 ,同理 )) 导致深度减 11 ,所以设 ((11))1-1

然后就有一个很牛逼的东西了,我们可以把一条长度为 x+yx+y 路径 u,vu,v 看成 uu 先走 xx1-1 走到 lca(u,v)lca(u,v) ,然后走 yy11 走到 yy ,发现这个玩意可以直接转到括号序上,因为我要是在递归 uu 所在子树和递归 vv 所在子树中间递归了其他子树,这是肯定会回溯上来的,所以算上这一段也不会对答案有影响,现在就是等于把题意转化成了:给定一个 1,11,-1 序列,单点修改,查询 max1lrnsumk+1,rsuml,k,k[l1,r]\max\limits_{1 \le l \le r \le n}sum_{k+1,r}-sum_{l,k},k\in[l-1,r] ,其中 suml,rsum_{l,r} 表示 [l,r][l,r] 这一段的权值和,这个玩意就可以用线段树维护了

具体来说,我们设 ansans 为要求的答案,现在枚举 kk[l,mid][l,mid] 还是在 (mid,r](mid,r] ,下面说的全局 ansans 最大值就是 l,rl,r 和区间 l,rl,r 相同,前缀/后缀 ansans 最大值就是固定前缀/后缀是 l/rl/ransans 最大值

kk[l,mid][l,mid] 时需要一个右区间的前缀权值 max\max 拼在后面,左区间需要一个后缀 ansans 最大值,kk(mid,r](mid,r] 时同理左区间后缀权值 min\min ,右区间前缀 ansans 最大值, 考虑前缀/后缀 ansans 最大值要怎么求,这个就是需要全局 ansans 最大值,然后前缀权值 max\max 还需要一个区间权值和,将这些都维护上就好了

code

线段树合并

我发现这玩意是纯傻逼,啥都过不去

#3046. 「ZJOI2019」语言

我们对于每个点求出所有他能到的点,不难发现这肯定是一个连通块,现在考虑怎么快速计算这个连通块内有多少点,因为原图是树,所以这个连通块一定是树,考虑什么点会跟当前点 uu 联通,肯定是经过了经过 uu 的链,所以我们将所有经过 uu 的链进行链覆盖,这个可以树上差分,而树上差分有一个很经典的东西,就是将 p1,p2,p3,,pnp_1,p_2,p_3,…,p_n 两两相连构成的连通块覆盖等于将 ppdfndfn 序排序,对于 pip_i 加一,对于 lca(pi,pi+1)lca(p_i,p_i+1)lca(p1,p2,p3,,pn)lca(p_1,p_2,p_3,…,p_n) 减一,这个题我们也可以这样,但是这样复杂度依然很高,继续优化

发现我对于每一个点都算很亏,发现答案其实就是加一的点的 depdep 减去减一的点的 depdep ,这样虽然复杂度没变,但是这个显然比刚才那个好优化

发现你子树里面的很多信息当前点也可以用,考虑线段树合并,线段树区间 l,rl,r 维护 dfndfn 序在 l,rl,r 时所有关键点和 11 号点能覆盖的点的数量,其中关键点就是每条链的链首链尾,对于一条链 u,vu,v ,我们让他在 u,vu,v 时都加入关键点 u,vu,vlca(u,v)lca(u,v) 的时候在都减回来,用上面说的那个 depdep 求方法维护一下就可以了

code

#2537. 「PKUWC2018」Minimax

不是很难

首先先想一下暴力 dpdp ,设 fu,jf_{u,j} 表示现在在节点 uu ,权值是 vjv_j 的概率,因为只有最多两个子节点,所以很好转移,时间复杂度 O(n3)O(n^3) ,可以优化,但是不重要

现在考虑用线段树合并优化这个东西,在合并区间 l,rl,r 的时候维护左/右儿子权值是 [v1,vl1][v_1,v_{l-1}][vl+1,vr][v_{l+1},v_r] 的概率,直接线段树合并就可以了

code

#3340. 「NOI2020」命运

好强的 dpdp 状态

考虑 dpdp ,设 fu,jf_{u,j} 表示我现在在 uu 节点,目前不满足要求的属于 QQ 人生经历在第 jj 层结束,特殊的, j=0j=0 时表示没有属于 QQ 的不满足要求的人生经历

转移就很好转移了,假设我现在在点 uu ,我要把 vvdpdp 转移过来,有柿子:

fu,j=i=0depufu,j×fv,i+i=0jfu,j×fv,i+i=0j1fv,j×fu,if_{u,j}=\sum\limits_{i=0}^{dep_u} f_{u,j}\times f_{v,i}+\sum\limits_{i=0}^{j}f_{u,j}\times f_{v,i}+\sum\limits_{i=0}^{j-1}f_{v,j}\times f_{u,i} ,第一个 \sum 是这条边为 00 ,后面两个 \sum11

这个玩意拿前缀和优化一下就是 O(n2)O(n^2) 的了,能拿到 40pts40pts ,考虑优化,因为刚刚做了 Minimax 我们知道线段树合并是可以搞前后缀 dpdp 的,所以直接线段树合并就可以了

code

李超树

P4097 [HEOI2013] Segment

李超树区间插线段板子

code 自认为写的还是能看的

P4655 [CEOI2017] Building Bridges

李超树优化 dpdp

这玩意做斜率优化 dpdp 都不用动脑子的

先搞一个 dpdp ,设 fif_i 表示我现在不拆第 ii 根柱子将桥从 11 架到 ii 的最小花费,不难发现转移是枚举上一个没拆的主子 fi=minj=1i1{fj+sumi1sumj+(hihj)2}f_i=\min\limits_{j=1}^{i-1}\{f_j+sum_{i-1}-sum_j+(h_i-h_j)^2\} ,好棒!

考虑优化,这是一个标准的斜率优化柿子,化简一下有

fi=minj=1i1{fj+sumi1sumj+hi2+hj22hihj}f_i=\min\limits_{j=1}^{i-1}\{f_j+sum_{i-1}-sum_j+{h_i}^2+{h_j}^2-2h_ih_j\}

fi=minj=1i1{fjsumj+hj22hihj}+sumi1+hi2f_i=\min\limits_{j=1}^{i-1}\{f_j-sum_j+{h_j}^2-2h_ih_j\}+sum_{i-1}+{h_i}^2

外面的常数都不用考虑,只看里面

将每一个 fjf_j 看成斜率是 2hj-2h_j 截距是 fjsumj+hj2f_j-sum_j+{h_j}^2 现在就等于有一堆直线,我们要查询位置 hih_i 的最小值,直接上李超树就行了

CF932F Escape Through Leaf

李超树线段树合并

fuf_u 表示从 uu 走到叶子节点的最小代价,转移就是 fu=minv{fv+au×bv}f_u=\min\limits_{v}\{f_v+a_u\times b_v\} ,这玩意就是一个标准的斜率优化,直接李超树合并就行了

至于怎么李超树合并,其实就是暴力,差不多就是把一颗李超树里面的所有直线全部插进另一颗里面,具体可以看代码,但是注意这个时间复杂度其实是 O(nlogn)O(n \log n) 的,因为线段树的深度是 O(logn)O(\log n) 的,是所以每条线段最多向下走 O(logn)O(\log n) 次,所以复杂度是 O(nlogn)O(n\log n)

code

兔队线段树

就是如果前缀会影响当前节点的情况的时候可以考虑用这个东西

P4198 楼房重建

下面说的楼的高度均除以 ii

对于线段树上每个点维护只考虑当前区间 [l,r][l,r] 的所有楼,能看见几栋楼,现在考虑怎么更新,我们定义一个函数 calccalc 表示考虑 [l,r][l,r][1,l)[1,l) 中楼的最高高度为 prepre ,这段区间内有多少楼能看见,设 midmid 为当前区间中点,mxmx 表示区间最大值,假设 [l,mid][l,mid]mxmx 小于 prepre ,那么左区间一个都不可能在当前区间答案里面,直接递归右区间就可以了,假设 [l,mid][l,mid]mxmx 大于 prepre ,那么右区间是不受 prepre 影响的,只用递归左区间,这样 calccalc 的时间复杂度是 O(logn)O(\log n) 的,不难发现有了这个就很容易算线段树上维护的东西了

注意到我要是想知道 (mid,r](mid,r] 受到 [l,mid][l,mid] 影响时的答案是需要减的,但是可能其他题维护的东西没有可减性,所以可以线段树每个区间维护在左区间影响下右区间的答案,这样就不用减法了

code

P9130 [USACO23FEB] Hungry Cow P

还是考虑兔队线段树,每个节点维护 cnt,sum,tocnt,sum,to 表示当前区间有几天有干草,右区间有干草的日期编号和和给后面的区间贡献多少干草,还是搞一个 calccalc 函数,要是左区间加上前面贡献过来的已经填满了就直接等差数列加起来就可以了,然后递归右区间,要是没加满就说明这个不会贡献到右区间,只用重新算左区间

code 代码非常比较抽象

CF671E Organizing a Race

segbeats

等我闲的没事的时候再写吧

主席树

#2016. 「SCOI2016」美味

看见异或直接上 trietrie ,然后不会做,trietrie 好像根本不能平移 👈🤣

但是主席树可以干这个玩意 😎

贪心从高位考虑到低位看一看这一位异或后能不能是 11 ,因为前面位的 0101 已经确定,所以这一位是 11 的位置肯定连续,减 xix_i 后也一定连续,在权值线段树上查就行了,区间交给主席树

时间复杂度 O(nlog2n)O(n\log^2 n)

code

关于线段树上的一些进阶操作 - command_block 的博客 - 洛谷博客 (luogu.com.cn)

cdq 分治

一个我非常讨厌的算法,虽然我也不知道为什么

平衡树

根号

分块

#6282. 数列分块入门 6 - 题目 - LibreOJ

操作:单点插入,单点询问

也没啥,数据随机就直接插就行了,数据要是不随机就定期重构一下,块长我也不知道设多少合适

code

#6284. 数列分块入门 8 - 题目 - LibreOJ

发现一次修改最多把两个块内数全相同的改成不是全相同的所以块内数不同的只有 O(n)O(n) 个,直接做就好了

不如线段树

#6285. 数列分块入门 9 - 题目 - LibreOJ

挺强的

早就忘了 🐸

O(nnlogn)O(n\sqrt{n \log n}) :等我想起来再写

O(nn)O(n\sqrt n)

要是询问两边块编号一样直接暴力

要是不一样发现可能的众数只有 n\sqrt n 种:散块部分出现的数和所有整块的众数

预处理出 Ai,jA_{i,j} 表示第 jj 个数在块 [1,i][1,i] 中出现次数, Bi,jB_{i,j} 表示块 [i,j][i,j] 的众数,直接做就行了

P5356 [Ynoi2017] 由乃打扑克

其实不是很难

首先有一个一眼的方法,对于每个块维护块内数排序后的结果,修改时散块直接暴力重构,整块打标记,查询就先二分答案,然后再对于每一个块二分求出有多少数小于等于第一个二分的答案,这样散块复杂度是 O(BlogB)O(B\log B) ,整块复杂度是 O(nBlogwlogB)O(\frac{n}{B}\log w \log B) ,其中 ww 是值域,将 logw,logB\log w,\log B 视为同阶,平衡复杂度后复杂度为 O(qnlogBlogB)O(q\sqrt{n\log B}\log B) ,寄!

发现我修改的时候散块加的东西都一样,所以可以改成归并排序来去掉一个 log\log ,现在平衡完复杂度是 O(qnlogB)O(q\sqrt n\log B) ,卡卡常就能过了

卡常也很简单,首先可以记录下来块内最大最小值,缩小二分范围,还有一个是再对于整块查询有多少个数 x\le x 时可以特判掉一个数都没有/所有数都 x\le x 的情况

块长大概 200200 最快

code

P3203 [HNOI2010]弹飞绵羊

对于每个位置求出跳出当前块的最小步数和跳出去后的位置,修改就暴力重构块,查询就暴力往后跳块,时间复杂度 O(qlogn)O(q\log n)

P4135 作诗

fi,jf_{i,j} 表示第 ii 个块到第 jj 个块的答案, sumi,jsum_{i,j} 表示数 ii 在块 11jj 出现次数,不难发现只要求出这连个答案就很好求了。

sumsum 很好求,现在考虑 ff 怎么求,我固定一个左端点 ii ,然后往后扫求出每个 jj 的答案,这样复杂度是 nnn \sqrt n ,可以接受

还有一个 O(n2ω)O(\frac{n^2}{\omega}) 的做法,到时候再写吧

#6546. 简单的数列题

莫队

CF617E XOR and Favorite Number

首先先做一个异或前缀和,现在就把问题转变成了区间内有多少点异或值为 kk ,发现这个东西好像什么 ds 都干不了,所以考虑莫队,莫队维护区间每个权值的点数,直接做就行了

分块相关杂谈 - command_block 的博客 - 洛谷博客 (luogu.com.cn)

莫队略解 - command_block 的博客 - 洛谷博客 (luogu.com.cn)

LCT

LCT小记 - command_block 的博客 - 洛谷博客 (luogu.com.cn)

树剖

KDT


小清新 ds
http://lnyxqwq.github.io/2023/08/15/小清新 ds/
作者
lnyx
发布于
2023年8月15日
许可协议