『区间最值操作 & 区间历史最值』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 计算,注意先考虑不规整的那一段。