【模板】带修莫队 / [国家集训队] 数颜色 / 维护队列

OJ: luogu

题目 ID: P1907

标签: 莫队TODO

日期: 2025-12-02 16:59

证明1: 修改(时间)t的可逆变化

时间T的swap,而 swap 带来的可逆性和时间轴的自由移动,才是让这个算法在逻辑上成立且代码极其优雅的“灵魂”。


[2 ... 6]

完全正确!一语道破天机。

你的这句话非常精准地总结了带修莫队的运行机理:“物理修改是全局的,答案统计是局部的”。

我们可以把这两点对应到代码上,会看得非常清楚:

cpp
inline void update(int change_idx, int L, int R) {
    int pos = changes[change_idx].pos;
    
    // --- 局部贡献 (View) ---
    // 只有当修改发生在你当前盯着的窗口 [L, R] 里时
    // 颜色的变化才会影响当前的统计结果 ans
    if (pos >= L && pos <= R) {
        del(pos); // 旧颜色贡献消失
    }
    
    // --- 全局修改 (Model) ---
    // 无论你在不在看这个位置,世界的真实状态(数组)必须改变
    // 这样是为了保证当你以后移动 L, R 经过这里时,看到的是正确的值
    swap(a[pos], changes[change_idx].val);
    
    // --- 局部贡献 (View) ---
    // 同上,只有在窗口里,新颜色才会计入统计
    if (pos >= L && pos <= R) {
        add(pos); // 新颜色贡献产生
    }
}

为什么必须是全局修改?

哪怕修改的位置 pos 远在十万八千里之外(不在 [L, R] 内),我们也必须执行 swap。 因为莫队的 LLRR 指针稍后可能会移过去。如果当时没改,等 L,RL, R 移过去的时候,看到的就是错误的旧颜色了。

恭喜你,理解到这一层,带修莫队对你来说已经没有秘密了!它本质上就是一个在三维空间中,一边游走一边维护局部统计量的算法。

change[k]想象成,通过一个地铁安检门,每次你经过这道门,保安都会把你身上的东西留下来,然后再把他的东西给你, 那么你经过这道门两次(你可以想象成你上班的时候,经过一次,下班的时候又经过一次),你的原来的东西就还原了