[题解]CF1209G2 Into Blocks (hard version)

思路

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

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

维护分界点比较平凡,因为 i[1,n],mki0,因此维护 mk 的最小值,若一个区间中有分界点则最小值就是分界点。

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

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

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

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

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

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

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;
}

作者:WaterSun

出处:https://www.cnblogs.com/WaterSun/p/18560817

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   WBIKPS  阅读(9)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示