『区间最值操作 & 区间历史最值』Day6

1 势能

1.1

有一类之前就见过的操作。区间取模区间开方。

开方是说在 \(\log \log B\) 次过后就不变了,所以这之前暴力即可。

取模则是说如果一个数能取模那么至少会减少一半,所以一个数最多暴力操作 \(\log B\) 次就没了。对于一个区间你维护最大值看是否需要递归进行操作即可。

上面的复杂度都是 \(O(暴力\times n\log n)\) 的。

1.2

区间开方,区间加,查询区间和。

为什么要按照 \(\max - \min\) 的情况来?

因为我们直接用标记处理 \(\max - \min \le 1\) 的情况。剩下暴力。

而每个区间暴力最多次数还是 $\log\log B $ 的,保证了暴力复杂度。

区间加,区间除,查询区间和,查询区间 min。

还是 max - min,考虑暴力复杂度。

因为你区间加是不会让 max - min 增多的,是 \(\log\) 次。

1.3

尝试对上面的情况进行总结。

你设置一个分界情况,一边暴力,一边打标记(且可以打标记);

只需要保证暴力的复杂度正确。

可以完成 T1。

查看他人代码发现没有处理区间覆盖,因为这种情况存在当且仅当之前 \(\max = \min + 1\),操作过后 \(\max = \min\),而这种情况会暴力下去 \(O(len)\) 次,消除 \(O(len)\) 的势能。

而之前的势能一共是 \(O(n\log n)\)

2 最值

结论:维护 Max, SecondMax;每次如果 Mintag < SecondMax 就暴力向下修改,否则可以直接改 Max。

感觉分析很乱,也许我曾经证出过,记结论。

3 历史版本

3.1

维护历史标记和当前标记,下传的时候子节点一定没有被这些标记标记过。(每次 pushup、pushdown 都需要传历史最值和当前)

更新的时候就把父亲的标记 \((x,y)\) 和儿子 \((x',y')\) 合并即可。

说到标记,你可以把它写成多原组的形式,重定义广义下的 + 和 max。

注意,+ 不一定满足交换律;但一定满足结合律。

3.2

两种标记叠加的情况。

如果你同时有加法和覆盖标,标记都是加法,突然来了一个乘法;那么接下来你维护的标记就不能有加法了,直接把覆盖标加上即可。

4 习题

Picks hates segment tree

为什么还支持加法啊。

因为你一次减少势能使得 maxn 和 sex 消失了一个,而加法不会影响相对大小。

V

标记的广义运算。放代码上来:

struct sb {
	int x, y;
	sb () {}
	sb (int _, int __ ) { x = _, y = __; }
	sb operator + (sb t) { return sb(x + t.x, Max(y + t.x, t.y)); }
};
sb max(sb x, sb y) { return sb(Max(x.x, y.x), Max(x.y, y.y)); }

struct node {
	sb _, a;
} T[N << 2];

void upd(int p, sb _, sb t) { // 看成是,从父亲节点传下来的,历史最大值,和现在的 tag 
	T[p]._ = max(T[p]._, T[p].a + _); T[p].a = T[p].a + t;
}

序列

莫队移动。

可以按照最小值的区间线性 dp 计算,注意先考虑不规整的那一段。

posted @ 2024-08-13 10:45  LCat90  阅读(10)  评论(0编辑  收藏  举报