YunQianDStricks_专题_LCA
JueFan 一只绝帆

YunQian DS tricks

另类线段树分治

与朴素的插入删除线段树分治不同,如果我们很容易解决先修改后询问(即可能要对修改做某种整体处理),并且询问的形式可以拆开贡献(例如 sum max...\text{sum max...},那么我们直接把修改挂在线段树的节点上,每次处理对整个区间的贡献。

线段树区间交的处理

若修改区间为 [L,R][L,R],询问区间为 [l,r][l,r],且 [L,R][L,R][l,r][l,r] 有交时贡献过去,且贡献形式可重(例:stst 表可维护的 max min and or gcd\text{max min and or gcd}),那么可以使用此 trick\rm trick

具体来说,我们开两棵线段树,一棵仅修改拆出来的 log\log 个区间,查询的时候查 询问区间的 log\log 个区间的祖先,另一棵修改拆出区间的祖先,查拆出的区间。

一棵修改包含的区间,查询相交的区间,一棵修改相交的区间,查询包含的区间。

不难发现,这种方式能查到所有拆出的线段中与自己有祖先关系的线段树节点,而两个区间有交的充要条件就是拆出的节点中存在祖先关系。

这个 trick\rm trick 应用极其广泛,它甚至可以扩展到上面的线段树分治中,解决时间轴区间询问。

颜色段均摊的应用

  • 树上每次覆盖到根是可以单 log\log 均摊的,每条重链都会有一个前缀死掉,所以维护单调栈即可。

但是这个一般不在瓶颈上,多数时候还带一个树状数组。

  • 很多时候覆盖问题可以利用颜色段均摊来保证在线段树上操作的是同一片区间(P10611)。
  • 一个数组,每个元素是一个区间,区间求区间并的长度,这个可以直接扫描线 + 颜色段均摊(P7126)。

例题:

使一颗心免于哀伤 https://yundouxueyuan.com/p/YDRG005D

nn 个函数不可重集,初始都为空,有 mm 个函数给定,有三种操作:

  • 区间插入函数 ii,如果已经插入了则忽略。
  • 区间删除函数 ii,如果没有就忽略。
  • s,l,r,xs,l,r,x,你需要从第 ss 个版本到当前版本中随便选一个时刻,在这个时刻的 [l,r][l,r] 这些函数集合中选出使得 f(x)f(x) 最大的函数。

n,q105n,q\le 10^5

首先先来解决没有 ss 的限制的情况,即时间轴上单点查询。

然后再尝试忽略删除,仅有插入。

这个时候你发现当且仅当插入区间 和 询问 [l,r][l,r] 有交才可以贡献,所以我们可以用两棵线段树完成这个过程。

可以线段树套李超树,两只 log\rm log

然后考虑删除,你发现你已经解决了先插入后询问,所以你首先可以利用颜色段均摊将所有操作变成不交的,然后利用颜色段均摊,将拆出来的区间 push\rm push 到时间线段树上,然后利用“另类的线段树分治”,处理每个修改对区间内所有询问的贡献。

然后就是时间轴的区间询问,再次套用区间交结构,一棵时间树上 push 少 query 多,另一边 push 多 query 少。

洛谷 P10611 故事结局

你需要维护一个大小为 n×mn \times m 的矩阵 AA,初始时其所有元素均为 00。题目还给出了一个长度为 mm 的序列 bb

共有 qq 次操作,分为两种:

  • 1 l r x v,对于 lirl \le i \le r,将 Ax,iA_{x,i} 修改为 vv
  • 2 l r x y,查询 maxi=lrmaxj=xy(Ai,j×bj)\max\limits_{i=l}^r \max\limits_{j=x}^y (A_{i,j} \times b_j)

对于所有数据满足:1n,m,q4×1051 \le n,m,q \le 4 \times 10^51bi1091 \le b_i \le 10^9

首先先考虑 b=1b=1

考虑树套树,乍一看外层单点修改内层区间修改好像很对,但是这题是覆盖而不是 cmax\rm cmax,这就导致一个非叶子的内层树无法直接区间修改。

换思路,外层区间修改内层单点修改,但是外层区间修改无法 pushdown,pushup\rm pushdown,pushup,怎么办?

注:check 一个线段树做法有没有假掉的有力方法是两种:先做一次大修改,再做若干次小询问;先做 nn 次小修改,再做一次大询问。

尝试套用区间交结构,只需要外层的两个区间有交就可以贡献了。

但它还是死了,你发现还是一样的错误,在你修改所有有交的的区间的时候,你做的这个 单点修改 并不是 cmax\rm cmax,所以实质上破坏了可重信息的条件。

考虑每行维护一个颜色段均摊,先把每个区间会切成什么样子求出来,在线段树上 push\rm push 的时候就直接加入细碎的小块,很显然不改变复杂度。

这样我们发现我们可以直接在一个点撤销掉加入这个点的贡献,所以我们可以用树套树的外层单点修改内层区间修改,内层每个节点套上 std::set,三只 log\log

然后你也可以套用区间交结构,每个叶子节点开一个 std::set 就可以了,两只 log\log,但是空间复杂度爆炸了。

离线,zkw线段树 + 可删堆解决。

现在考虑 bb 这个权值,如何兼容这个权值呢?

我们来剖析一下两棵线段树:

  • 修改相交,查询包含的这棵解决的是修改是查询子区间的问题,所以我们应该在修改的时候乘上区间 maxb\max b
  • 修改包含,查询相交的这棵解决的是查询是修改子区间的问题,所以我们应该在查询的时候乘上区间 maxb\max b

但这里涉及一个问题,第一棵树怎么实现?

朴素的实现是定位到所有线段树节点,再对每个节点分别做祖先链修改,但是似乎多了一个 log\log

但你发现你要求的等价于每个线段树节点与修改区间的交的 maxb\max b,你发现这与我们 query maxb\text{query maxb} 的时候做的事情是一样的!!!

所以这里要先递归下去,求出 maxb\max b 之后再修改。

同理,query 的时候也要乘上区间交的 maxb\max b

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
总字数 231.7k 访客数 访问量