【笔记】数据结构专题

恐怖

一大堆 Ynoi,一大堆不会的

以后再来吧

https://vjudge.csgrandeur.cn/article/3908

8.5 数据结构

扫描线

P5490 【模板】扫描线

对坐标离散化。维护 \(a,b\)\(a\) 是相邻两个矩形高度差,\(b_i\) 初始全零,操作是 \(b[l,r]+=v\),询问 \(\sum_{i} a[b_i\geq 1]\)。维护 \(\min,\sum\) 并合并。

P4605 [SDOI2018] 物理实验

线段有自己对应的权重,就是它的长度。加入线段树时维护离导轨最近的线段,这样就能得到这条分割出的 \(x\) 轴上的线段的权重,然后查询。

CF704E Iron Man

树剖改成链,变成一大堆链是否有交,插入时只考虑相邻(两条线段中间没有别的,一个上一个下)的线段。

扫描线扫描时间,时间是它在 \(x\) 轴上加入和删除的时间,然后维护 std::set,去动与它相邻的线段,判交/修改权重并查询。

P6106 [Ynoi2010] Self Adjusting Top Tree(?)

对于 \(x_1<x_2,y_1>y_2\),维护与上界有交并且在红色虚线下的长度,和与右界有交并且在黄色虚线左边的线段长度和。红线上移时,线段的长度,关于 \(y\) 形如 \(ky+b\) 的一次函数,所以维护 \((\sum k)y+(\sum b)\),右边的用树状数组维护一下。

变形扫描线

\(n\) 个区间,多次询问一个大区间有多少个小区间是给定的?

\(x=1\to n\),if

  • \(x=r_i\to a_{l_i}:=a_{l_i}+1\)
  • \(x=R\to ans=query(L,n)\)

CDQ 分治

P3810 【模板】三维偏序(陌上花开)

对着 \(a\) 排序后分治,变为 \(j\in [l,mid]\)\(i\in [mid+1,r]\) 的贡献,\(f(i)=\sum_{j<i}[b_j\leq b_i][c_j\leq c_i]\)。对 \(b\) 扫描线,树状数组维护 \(c\) 的值域前缀和。

P4690 [Ynoi2016] 镜中的昆虫

没有修改,维护 \(pre_i\) 表示上一个颜色与它相同,\(ans=len-\sum_i[pre_i\in[l,r]]\)。可以扫描线做。

\((t,x,y,v)\) 表示时刻 \(t\)\(pre_y=x\),询问就是 \((\leq t,\geq l,\leq r)\)

修改就颜色段均摊,\(pre\) 只在修改颜色段个数个地方要修改。

CF568E Longest Increasing Subsequence

数字重复用是没有意义的。

\(f_{a_i\neq -1}=1+\max_{0\leq j<i,a_j\neq -1,a_j<a_i}\{f_j\}\)。 还要中间能填的个数,就是 \(\min(c_i-c_j,s_{a_i}-s_{a_j})\)。钦定方向之后 cdq 分治优化 dp。

P5979 [PA2014] Druzyny

\(rng(i,j)=[\max l(i,j),\min r(i,j)]\)

\(f_i=\max_{i-j\in rng(j,i),j<i} f_j+1\)

cdq,\(i-j\in rng(j,mid)\land i-j\in rng(mid+1,i)\)

\(\Rightarrow i\in j+rng(j,mid)\land -j\in rng(mid+1,i)-i\)

预处理右边这些,用线段树维护。

树上二分

线段树上 kth

类似平衡树地做,\(k\leq siz_{lc}\) 则左子树,否则右子树。

P6619 [省选联考 2020 A/B 卷] 冰火战士

离散化,设 \(A\) 是前缀和,\(B\) 是后缀和,则求 \(\max(\min(A,B))\)\(A\) 上升,\(B\) 下降,必只有一个交点。对线段树维护左边 \(A\) 的值和右边 \(B\) 的值,在线段树上二分,根据 \(A_r\)\(B_r\) 的关系决定去哪个子树。

GYM104090J Painting

这是把平面划分成很多块,维护每一块的顶点。画一条线就是把区域分成两部分,暴力维护。找停止点,就是知道每个顶点的坐标,然后二分每个点,判断每个点在线的上面还是下面。存点从 \(y\) 轴上较高的那个店开始顺时针存。

线段树合并与分裂

P4556 【模板】线段树合并

树上差分之后线段树合并。

复杂度:每次合并的时候遍历到就删去,总数 \(O(n\log n)\)

P5494 【模板】线段树分裂 (待写)

\(\leq k\)\(>k\) 的分成两个线段树?像 fhq,但是要再复制一遍,最多一共两个根,三个儿子节点,刚好等于的时候就增加一个点。每次分裂只增加一条链,所以空间 1log。

P5612 [Ynoi2013] Ynoi(1log 空间)

维护形如 \((sort(S_1),x_1),(sort(S_2),x_2),\cdots\) 的对子,表示 \(S_i\) 已经排好序,\(x_i\) 是异或 tag,每个对子用一个 01-trie 的值域线段树维护。

操作二:split 一些对子(注意到这些对子的分裂可以直接按值域分裂而不是下标,因为排好序了),暴力合并所有中间的对子,形成新的对子。

将每个对子的左端点记录在线段树上,tag 打在另一棵线段树上,那么暴力合并的时候就是在线段树上先找到 tag 的值,然后再合并。

操作一:线段树。

操作三:线段树。

然后就是 \(O(n)\) 的 01-trie 了,不在讨论范围之内。

P5044 [IOI2018] meetings 会议(?)

暴力是区间 DP。\(f(l,r)=\min(f(l,i-1)+(r-i+1)h_i,f(i+1,r)+(i-l+1)h_i)\) 其中 \(i\) 表示 \([l,r]\) 中最大值下标。

对笛卡尔树节点记前后缀 DP 值。然后拆出来一堆什么什么东西???然后线段树合并两个儿子。

UOJ418 【集训队作业2018】三角形

加入 \(son_u\)——加入 \(u\)——删掉 \(son_u\)——删掉 \(u\)

时光倒流变成:先加父亲,再加儿子,然后维护 \((\Delta sum,pmx)\) 表示一个操作需要多用多少个石子,当前最大前缀和。例如加入 \(u\) 后每次加儿子就形如 \((w_j,pmx+w_j)\) 的东西,最后删掉儿子就是 \((-\sum_j w_j,?)\) 的东西。合并显然吧!

只考虑 1 号节点的操作序列,我们发现如果不保证先父亲后儿子的性质,那么显然是所有 \(\Delta<0\) 的先做了,\(pmx\) 从小到大;\(\Delta>0\) 的后做,\(pmx\) 从大到小。要考虑先父亲后儿子,只需使每对父子关系的点,如果错了,那调整后一定是搞完父亲立马搞儿子,用并查集把它们并起来就行。所以我们得到了 1 号点的操作序列。

欲将得到每个点的操作序列,把 1 号的操作序列重新按时间放回去,线段树合并。

兔队线段树

maintain 需要一次 query 的线段树就是兔队线段树啦

P4198 楼房重建(待写)

改编题

\(a_i\) 值域线段树,\(sum\)\(b_i\) 的和,\(end\) 是最后一个结束洗澡的人的时间。

CF671E Organizing a Race(?)

\(G\)\(g\) 的前缀和,\(W\) 同理。判定 \([l,r]\),就是判定是否 \(\forall i\in [l,r],s.t.G_i-G_{l-1}\geq W_i-W_{l-1}\),另一边 \(G_r-G_i\geq W_r-W_i\)。就是 \(G_i-W_i\geq G_{l-1}-W_{l-1}\land G_r-W_r\geq G_i-W_i\),对着这些相似的东西记为 \(a,b\)。一次操作就是后缀加。枚举 \(r\),可以向左找很多合法的点,用掉操作。

\(\max(a_i-\min_{l\leq j\leq r}a'_j)\leq k-\text{已经加的}\)。在线段树上维护巨大的 \(a_i-\min_{1\leq j\leq i}b_j\),考虑合并,还是一大堆东西。最后线段树二分。

线段树分治

P5787 二分图 /【模板】线段树分治

P3206 [HNOI2010] 城市建设

线段树分治,线段树每个节点上都存了一些边,它们的权值在那一段时间 \([l,r]\) 保持不动,称为静态边;在那一段时间有 \(r-l+1\) 个边的权值发生改变,称为动态边。从根开始 dfs,在外面动态维护一个可撤销并查集,

  • 就是说你 dfs 到 \([l,r]\) 的时候并查集里面有一些边已经加入。
  • 然后甚至还有一些边从上面扔过来,属于静态边(后面进行循环论证)。

在这个节点上我们做以下事情:

  • 将动态边的权值设为 \(-\infty\),在原有并查集基础上和静态边一起跑 MST。
    • 现在还在 MST 中的静态边,以后无论加哪些动态边(向下递归时动态边的集合一定是现在动态边的子集),它都会在 MST 中。
    • 撤销这次 MST,将刚才说的“还在 MST 中的静态边”加入并查集,不再动它。
  • 将动态边的权值设为 \(\infty\),在现有并查集基础上和静态边一起跑 MST。
    • 现在还不在 MST 中的静态边,说明它太菜,反正递归下去还要加这些静态边,这条边打不过了!
    • 撤销这次 MST,将打不过的边弃掉。
  • 向左右儿子递归,带着刚才没被弃掉的、没被加入并查集的边继续它的征程。

证明复杂度:

  • 观察到:并查集连通块个数不超过 \(O(len)\),下传边数不超过 \(O(len)\)
  • 因为在加入 \(len\) 个动态边之后整个图连通,所以结论一成立。
  • \(O(len)\) 个连通块的并查集,最多加入 \(O(len)\) 条边,所以向下递归的边肯定也是只有 \(O(len)\) 条。

UOJ503【JOISC2020】扫除

部分分:\(x_i\leq x_{i+1},y_i\leq y_{i+1}\),不加点:每次改变连续区间。

对每个点 \(i\) 求出第一次被推的时间,推完之后就满足上面那个东西了呢!那么就是要求这个时间,就是矩形查询。

但是加点非常不好做,用线段树分治 1log 代价解决。

P6109 [Ynoi2009] rprmq1(?)

好像是历史最值问题,不会了

树套树

P3380 【模板】二逼平衡树(待写)

第一维线段树维护位置,第二维平衡树维护值。

给定 \([l,r]\) 询问有关 \(k\) 的信息?将 \([l,r]\) 拆成线段树上区间,每个都在平衡树上二分。

kth?首先有一个 3log 的二分做法。实际上我们可以互换一下,第一维是权值线段树维护值,第二维是平衡树维护位置,kth 直接线段树二分,保证 \([l,r]\) 区间内得有那么多值。

P5471 [NOI2019] 弹跳

容易想到一个线段树优化建图的做法,但听说过不去。

观察 dijkstra 的过程,每个点第一次拿出来,就不会再继续更新,所以我们有做法:从起点开始,进行松弛操作,明显这个松弛操作更新的是一个值,那么我们把这个松弛到的矩形和最短路长度放入堆中。每次拿出矩形,反复进行:在矩形中找一个点,枚举这个点松弛,删掉这个点 的操作,不难发现这样非常正确。

P3688 [ZJOI2017] 树状数组(?)

维护操作二所有点对的答案。修改变成矩形修改。树套树。

可持久化

左偏树

堆。\(dis_x\) 表示 \(x\) 走到最近叶子的距离。保证 \(dis_l>dis_r\) 的堆是左偏树,\(dis_x=dis_r+1\)

合并就是 fhqtreap。因为 \(dis\) 不超过 1log,所以复杂度为 1log。

可持久化:开一个新的点可持久化即可,因为修改点很少。

P4899 [IOI2018] werewolf 狼人

建两个 Kruskal 重构树,变成查询两个子树是否有交。在 dfn 序并拆掉一维之后,变成查询一个区间内是否有数在区间内,主席树问题了。

杂题

P2305 [NOI2014] 购票

自上往下 DP。\(dis_u\)\(u\)\(1\) 的深度,然后有:

\[dp_u=\min_{v\in anc(u),dis_u-dis_v\leq \ell_u}\{(dis_u-dis_v)p_u+q_u+dp_v\}\\ =dis_u p_u+q_u+\min_{v\in anc(u),dis_v\geq dis_u-\ell_u}\{-dis_vp_u+dp_v\}. \]

口胡一个!李超线段树,dfs 下去的时候加入线段树套李超线段树然后想怎么做就怎么做!

posted @ 2023-08-05 20:22  caijianhong  阅读(42)  评论(0编辑  收藏  举报