闲话 22.7.25

闲话

怎么普及组月赛我才过两个题啊我怎么这么菜啊

发现幻影我会唱第一段的前几句和第二段除了前几句以外的部分所以一直在串
而且还有第三段 我也只会唱第三段最后一句
串得更严重了

?月光昨天想了一天该怎么唱都没想出来
今天突然又会唱了 离奇
其实这种事确实挺多

《00:01:32》

总感觉最近考试时发现了很多智械危机
包括但不限于

  1. 手模结果和程序输出不一样 惊慌失措后发现程序是对的

  2. 拍和正解拍了几组数据发现是对的 突然错了
    看了看发现拍写错了
    ?我正解为啥和它输出一样

连续段问题

首先定义一下连续段。形式化地,对于一个序列 \(a\),若其中元素数值在值域上连续,则称该序列是连续的。任何一个排列都是一个连续段,单一元素一定是连续段。

CF526F

给定一个排列,计算其中连续段的数量。

我们发现,对于一个连续段,我们需要考虑的只有其长度、最大值与最小值。定义 \(\max(a)\)\(a\) 序列的最大值, \(\min(a)\)\(a\) 序列的最小值。由于连续段通常是来自其他序列的子段,因此定义其在原序列上的位置为 \([l,r]\)。此定义是良的。由此定义可以得到连续段长度,即 \(\text{length}_ a = r - l + 1\)。根据此定义,我们有判断一个无重复序列连续性的充要条件:

\[\text{length}_ a = \max(a) - \min(a) + 1 \]

该条件形式化地描述了连续段的重要特点:其元素数量与值域范围的大小相同。

我们考虑如何通过枚举右端点的方式迅速判定连续段个数,以及如何利用原先信息加速求解。

考虑判断条件的另一种形式:

\[\max(a) - \min(a) - \text{length}_ a = -1 \]

这启发我们通过等号右边信息的值判断其是否为连续段。对于确定的右端点,我们维护以其左端所有点为左端点的\(\text{eigen} = \max - \min - \text{len}\)信息,这点可以通过两次修改与初始化的方式在线段树上叶子节点实现。对于非叶子节点,取其子节点 \(\text{eigen}\) 的最小值,并在上提时维护最小值的数量信息。在每次维护完毕后,从原点查询 \(\text{eigen}\) 最小值的数量,该值即为所求。

下面证明维护最小值的正确性。对于一段给定的区间,由于其来自一个排列,其中元素定不相同。这样,其就会取到 \(\text{length}\) 个不同的值。由于当这些值连续时所占值域范围最小,更换任意一个值都会使得值域范围增大,因此有不等式

\[\text{length}_ a \le \max(a) - \min(a) + 1 \]

当且仅当区间为一个连续段时取等。

将不等式改写:

\[\max(a) - \min(a) - \text{length}_ a \ge -1 \]

这表明任意不连续段的 \(\text{eigen}\) 定大于连续段的 \(\text{eigen}\)。而对于任意确定的右端点,当取左端点为右端点时该段连续。这表明在取值过程中最小值定可取到,因此取最小值的数量作为连续段的数量是正确的。 \(\blacksquare\)

现在考虑如何加速求解。

这是很简单的。当我们使用右端点右移一位的策略时,每次只会在右端增加 \(max\)\(min\) 的可能取值位置。使用单调栈维护最大最小值,弹栈时删除以栈首元素为最大最小值的区间中栈首元素的贡献,随后加入当前位置,并在线段树上加入当前元素的贡献。代码如下:

void upd(int p, int l, int r, int L, int R, int x) 
	{ /* 维护[L,R]区间加x,其中p为线段树节点编号,[l,r]为当前节点所维护区间*/ }

int stk1[N], top1; // 最大值
int stk2[N], top2; // 最小值

while (top1 and a[stk1[top1]] < a[i]) 
	upd(1, 1, n, stk1[top1-1] + 1, stk1[top1], -a[stk1[top1]]), top1--;
while (top2 and a[stk2[top2]] > a[i]) 
	upd(1, 1, n, stk2[top2-1] + 1, stk2[top2], a[stk2[top2]]), top2--;
	// 在弹栈过程中消除最大/最小值的贡献
	// 每个元素的贡献是以他为最大/最小值的区间,即[ stk1[top1-1] + 1, stk1[top1] ]
upd(1, 1, n, 1, i, -1);
// 区间长度的影响
stk1[++top1] = stk2[++top2] = i;
upd(1, 1, n, stk1[top1-1] + 1, i, a[i]);
upd(1, 1, n, stk2[top2-1] + 1, i, -a[i]);
// 加入当前元素的贡献
posted @ 2022-07-25 08:35  joke3579  阅读(154)  评论(5编辑  收藏  举报