正睿OI 暑假 B Day6

主要记录一下思想,避免以后忘记

不会发在 Luogu Blog 的。


众所周知,线段树有两种情况:

  1. 单点修改,区间询问:对于每个线段树上的区间维护一个区间的信息之和。
  2. 区间修改,单点/区间询问:每次修改时,对区间打上一个修改标记,在询问和修改时,一旦访问到一个区间,就将它的标记下传。

情况一中,我们只需要支持:区间信息之间的合并

情况二中,除了 区间信息之间的合并,还要考虑 标记之间的合并区间信息与标记的合并。也就是说,理论上来讲,只要能快速进行这三种操作,标记和区间信息可以是任何东西。

情况二的模板如下:

struct data;
struct lazy;
data res[N<<2];lazy tag[N<<2];
void givetag(int o,lazy v)
{
    tag[o]=mergetag(tag[o],v);
    res[o]=apply(o,v);
}
void push_down(int o)
{
    givetag(ls(o),tag[o]);
    givetag(rs(o),tag[o]);
    tag[o]=lazy();
}
void modify(int o,int l,int r,int x,int y,lazy v)
{
    if(x<=l&&r<=y){
        givetag(o,v);
        return;
    }
    push_down(o);
    int mid=(l+r)>>1;
    if(x<=mid)modify(ls(o),l,mid,x,y,v);
    if(y>mid)modify(rs(o),mid+1,r,x,y,v);
    res[o]=mergedata(res[ls(o)],res[rs(o)]);
}
data query(int o,int l,int r,int x,int y)
{
    if(x<=l&&r<=y)return res[o];
    push_down(o);
    data ans=data();
    int mid=(l+r)>>1;
    if(y<=mid)return query(ls(o),l,mid,x,y);
    else if(x>mid)return query(rs(o),mid+1,r,x,y);
    else return mergedata(query(ls(o),l,mid,x,mid),query(rs(o),mid+1,r,mid+1,y));
}
View Code

以下题目如没有特殊说明,n,Q 范围均为 $10^5$

以下题目没有代码实现,没有题目来源,解法只是口胡,主要锻炼思维。


 线段树例题2

给定序列列 a[1…n],支持单点修改,每次求区间单调栈大小。

解法

区间单调栈就是说,从左至右,如果当前数大于栈顶则入栈,求最后栈大小。

即求 $a[i]=\max_{j=1}^{i}a[j]$ 的个数。

单点修改,所以只需考虑区间信息之间的合并:对于一个节点 $p$,已知其两个子区间 $ls$ 和 $rs$ 的单调栈大小(记为size),如何求出 $p$ 的单调栈大小?

$ls$ 的单调栈一定在 $p$ 的单调栈里,所以我们只需要计算右半部分的贡献,记做 calc(me,pre)。其中 pre 为之前的单调栈顶(lmax)。

那如何计算 calc(me,pre) 呢?继续递归。注意接下来的 $ls$ 和 $rs$ 为 calc 函数中的 me 节点的左右儿子。

  1. pre>lmax 直接计算 calc(rs,pre),因为左半部分无法进入单调栈,没有贡献。
  2. pre<=lmax 答案为 calc(ls,pre)+calc(rs,lmax),递归处理,右半部分的 pre 即 lmax。

但是这个复杂度显然是不对的,因为最坏情况可以一直通过第二种情况分下去,复杂度 $\mathcal O(n)$。

如何优化呢?观察发现有这么一个性质:calc(rs,lmax)=size-lsize,而 size 可以预处理(存疑?)

复杂度 $\mathcal O(Q\log^2 n)$

未完待续……To Be Continued……


线段树例题3

有一个序列列 a[1…n],每个 a[i] 是 (c,x,y) ,表示颜色和坐标。要求支持单点修改以及区间询问:颜色不同的曼哈顿距离最大的一对点的距离。

解法

单点修改:只需考虑区间信息的合并。

套路:对于曼哈顿距离的处理有一个小技巧:

$$|x_1-x_2|+|y_1-y_2|=\max\left\{\begin{aligned} x_1-x_2+y_1-y_2 \\ x_2-x_1+y_1-y_2 \\ x_1-x_2+y_2-y_1 \\ x_2-x_1+y_2-y_1 \end{aligned} \right.$$

那最暴力的就是搞四颗线段树,求出每个情况,再取 max。

以第一种情况为例:$(x_1+y_1)-(x_2+y_2)$,我们希望前者尽量大,后者尽量小。

如果不考虑颜色的限制,只需求出区间内 $x+y$ 的最大值和最小值即可。加上颜色的限制后,除了最大值,还需要求出与最大值颜色不同的次大值,同理需要与最小值颜色不同的次小值。这样,如果最大值和最小值颜色不同,则直接出答案;如果颜色相同,则有最大值-次小值和次大值-最小值两种情况。

同理,第二种情况就是 $(y_1-x_1)-(y_2-x_2)$,维护 $y-x$ 即可。

区间信息合并画个图讨论一下就好,不再赘述。

 

其实,这道题的本质是曼哈顿距离和颜色不同的处理,这两个技巧才是重点,线段树只是一个外壳。


线段树例题4

给定 a[1…n],要求支持单点修改,以及区间询问 a[L…R] 不能组成的最小的数(01背包组不出来的数)。a[i] 到 10^9,正数(负数似乎没法做)。

解法

先考虑:如果给定一些数,如何找出不能组成的最小的数。

从简单入手的话,先看 1 有没有,没有就直接输出 1。有了 1,还必须有 2,此时就能组出 1~3。于是可以从数学归纳法的角度思考。

假设现在已经可以组出 $1\sim x$,又来了一个数 $y$。如果 $y>x+1$,则 $x+1$ 不能组出;否则我们就可以将能组出的值域扩大至 $1\sim x+y$。

从这个角度出发,假设我们现在可以组合出 $1\sim T$,那么所有的 $a[i]\le T+1$,都可以作为“燃料”“投入”进去,最后能组成的范围就是 $T+\sum_{a[i]\le T+1}a[i]$。

这样算法就比较明显了,一开始是 $T=0$,然后每次询问下标在 $[l,r]$、值域在$[LST_MAX_a+1,T+1]$(不能选重复的)的所有值之和,更新 $T$。

因为还需要支持单点修改,所以需要用树套树解决。不然的话可以用主席树(主席值域线段树?)。树套树:下标线段树套权值线段树,先按下标分成 log 个区间,再在每个区间询问值域。

$T$ 的更新过程是 log 值域(比较显然但不会证),树套树是两个 log,合起来就是 $\mathcal O(Q\log a·\log^2n)$。

据某些消息,本题目在考场上时限开到了 15s 之多,所以三个 log 随便过,但是实际上时限开错了,正解是非常巧妙两个 log。

我们把序列按照值域从小到大分成很多段,每一段的范围是 $[2^{i-1}-1,2^i]$。

那么 $T$ 一开始在 $[0,1]$,我们需要判断 $T$ 能不能“吸收”下一段,假设是 $[2^{k-1},2^k]$。判断方法就是找出 $i\in [l,r],a[i]\in [2^{k-1},2^k]$ 的最小的 $a[i]$,假设为 $y$,如果 $y$ 比 $T+1$ 大,则结束更新,否则 $T$ 就会变大,且 $T\ge 2^{k-1},y\ge 2^{k-1}$,于是 $T+y\ge 2^k$。那么这整个一段都可以用来更新 $T$。接着到下一段继续运行。

具体实现可以对每一段都建立一棵线段树,询问就是 $\mathcal O(Qlog^2 n)$(?),修改也只会改个别几个线段树(?)。


线段树例题7

好像是个 CF 题,最后有坑,建议结合代码理解。

给定序列 a[1…n] 以及 k,d,求一个最长的区间,使得最多加入 k 个数后 sort 后是一个公差为 d 的等差数列。n<=200000。

解法

一个区间是公差为 d 的等差数列等价于:

  • 所有数模 d 一样
  • 没有重复的数

那就先把序列分为很多段,每一段模 d 一样,假设 x%d=c。

接着再进行一步变化:$x\rightarrow \dfrac{x-c}{d}$。这样就把公差变成了 1。

于是最少就要加 $[\max(L,R)-\min(L,R)+1-(R-L+1)]$。

实现可以从小到大枚举 R,求最小的 L 满足 a[L...R] 无重复(双指针记录上一个数出现在哪即可),且上式 <=k。

现在就剩下这么一个玩意儿:$w[L]=\max(L,R)-\min(L,R)+L\le k+R$,找最小的 L。

据说可以用单调栈搞,留坑代填。复杂度 $\mathcal O(n\log n)$


线段树例题8

给定序列列 a[1…n],Q 次询问 a[l…r] 中相差最小的两个数的差。n,Q<=200000。

解法

最直接粗暴的方法:离线搞莫队,加上个 set 来维护数值,$\mathcal O(n^1.5\log n)$。

正解:咕了,还没学会。


线段树例题

posted @ 2020-08-23 22:05  ytmmy  阅读(158)  评论(0)    收藏  举报