[题解]CF1209G2 Into Blocks (hard version)

思路

首先考虑 Easy Version 没有修改的做法。记 \(l_i,r_i\) 表示颜色 \(i\) 第一次和最后一次出现的位置,若想颜色 \(c\) 不变,必须使得 \([l_c,r_c]\) 区间所有不为 \(c\) 的点都要变,但是可以中间有一个点的颜色为 \(x\),满足 \(r_x > r_c\),那么此时 \((r_c,r_x]\) 这段区间中也必须变成 \(c\) 的颜色。这启发我们将序列分段,使得段中出现的所有颜色都仅在这个段中出现,那么每一段都可以保留一种颜色不动,因此保留段中众数即可。

考虑带修怎么做。一个经典的转化:对于每一个颜色 \(c\)\([l_c,r_c]\) 打上一个标记,记 \(mk_i\) 表示 \((i,i + 1)\) 被标记的次数。那么两个相邻 \(mk\)\(0\) 的位置就是一段。

维护分界点比较平凡,因为 \(\forall i \in [1,n],mk_i \geq 0\),因此维护 \(mk\) 的最小值,若一个区间中有分界点则最小值就是分界点。

维护每一段的众数,注意到每一段必定不存在在段外有与段内相同颜色的点,因此段内的众数就是在段内出现过的颜色所出现过的数量,不妨在 \(l_c\) 时记录 \(c\) 出现过的数量,记为 \(val\)

其实我们可以直接把信息丢到线段树上。还需维护三个信息 \(sum,lval,rval\)

  • \(sum\):表示在区间中完整段的贡献,对应在 \(mk\) 上就是被两个零包起来的段的答案。
  • \(lval\):表示区间中左端点所在段中的权值最大值,对应在 \(mk\) 上就是区间左端点至区间最左零这段区间的权值最大值。\(rval\) 同理。

考虑如何在线段树上合并这些信息(将 \(ls,rs\) 合并为 \(u\)),假设 \(u\) 区间至少包含一个分界点:

  • 若仅在 \(ls\) 区间中出现了分界点,即形如:[xxx0xx][xxxxxx]
  • 若仅在 \(rs\) 区间中出现了分界点,即形如:[xxxxxx][xx0xxx]
  • 若在两个区间中都出现了分界点,即形如:[xxx0xx][xx0xxx]

不难发现如果两个 \(u\) 区间没有分界点,则按照上述任意转移都是正确的。因为 \(mk_1\) 不一定为 \(0\),所以在线段树上 \([1,n]\) 的节点的 \(sum\) 信息中可能不包含 \(1\) 所在段的贡献,因此在 \(1\) 前面插入一个 \(mk\)\(0\) 的哨兵即可。

Code

#include <bits/stdc++.h>
#define re register

using namespace std;

const int N = 2e5 + 10;
int n,q;
int arr[N];
set<int> col[N];

inline int read(){
    int r = 0,w = 1;
    char c = getchar();
    while (c < '0' || c > '9'){
        if (c == '-') w = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9'){
        r = (r << 3) + (r << 1) + (c ^ 48);
        c = getchar();
    }
    return r * w;
}

struct{
    #define ls(u) (u << 1)
    #define rs(u) (u << 1 | 1)
    #define val(u) (tr[u].val)

    struct value{
        int sum,val,lval,rval,Min;

        value friend operator +(const value &a,const value &b){
            value res;
            res.val = max(a.val,b.val);
            res.Min = min(a.Min,b.Min);
            if (a.Min < b.Min){
                res.sum = a.sum;
                res.lval = a.lval; res.rval = max(a.rval,b.val);
            }
            else if (a.Min > b.Min){
                res.sum = b.sum;
                res.lval = max(a.val,b.lval); res.rval = b.rval;
            }
            else{
                res.sum = a.sum + b.sum + max(a.rval,b.lval);
                res.lval = a.lval; res.rval = b.rval;
            }
            return res;
        }
    };

    struct node{
        int l,r,tag;
        value val;
    }tr[N << 2];

    inline void build(int u,int l,int r){
        tr[u] = {l,r};
        if (l == r) return;
        int mid = l + r >> 1;
        build(ls(u),l,mid); build(rs(u),mid + 1,r);
    }

    inline void calc(int u,int k){
        val(u).Min += k; tr[u].tag += k;
    }

    inline void pushup(int u){
        val(u) = val(ls(u)) + val(rs(u));
    }

    inline void pushdown(int u){
        if (tr[u].tag){
            calc(ls(u),tr[u].tag); calc(rs(u),tr[u].tag);
            tr[u].tag = 0;
        }
    }

    inline void update(int u,int x,int k){
        if (tr[u].l == tr[u].r) return (val(u).val = val(u).lval = val(u).rval = k),void();
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (x <= mid) update(ls(u),x,k);
        else update(rs(u),x,k);
        pushup(u);
    }

    inline void modify(int u,int l,int r,int k){
        if (l > r) return;
        if (l <= tr[u].l && tr[u].r <= r) return calc(u,k);
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(ls(u),l,r,k);
        if (r > mid) modify(rs(u),l,r,k);
        pushup(u);
    }

    #undef ls
    #undef rs
    #undef val
}T;

inline void add(int x){
    if (col[x].empty()) return;
    int l = *col[x].begin(),r = *col[x].rbegin();
    T.update(1,l,col[x].size());
    T.modify(1,l,r - 1,1);
}

inline void del(int x){
    if (col[x].empty()) return;
    int l = *col[x].begin(),r = *col[x].rbegin();
    T.update(1,l,0);
    T.modify(1,l,r - 1,-1);
}

#define answer (n - T.tr[1].val.sum - 1)

int main(){
    n = read() + 1,q = read();
    for (re int i = 2;i <= n;i++) col[arr[i] = read()].insert(i);
    T.build(1,1,n);
    for (re int i = 0;i <= 2e5;i++) add(i);
    printf("%d\n",answer);
    while (q--){
        int x,k;
        x = read() + 1,k = read();
        del(arr[x]); col[arr[x]].erase(x); add(arr[x]);
        del(k); col[arr[x] = k].insert(x); add(k);
        printf("%d\n",answer);
    }
    return 0;
}
posted @ 2024-11-21 15:22  WBIKPS  阅读(34)  评论(0)    收藏  举报