F题题解

我们用一张按起点排序的有序映射来维护所有已经被赋值过的区间,每个映射项记录该区间的终点和对应的值。在对任意区间 [l,r] 执行赋值操作之前,先在 l 和 r+1 处将可能跨越端点的原有区段一分为二,这样就能保证待修改区间恰好由若干完整的映射段组成;然后依次删除这些旧段,同时在一个哈希表中扣减相应值的元素个数,如果某个非零值的计数变为零,就将当前不同元素种类数减一。删除完毕后,再在起点 l 处插入一个新段,长度为 r−l+1,如果新值此前未出现过,就先将种类数加一,再把该区间长度加入该值的计数中。

由于数组在最大索引之后默认都是零,所以我们一开始就把整个 [1, N] 区间初始化为值 0,并在计数时无论零的个数如何变化都不减少种类数,保证零始终被视为一种存在的值。所有的分段、删除和插入操作都在 map 上以对数时间完成,每个段最多被拆分和删除一次,因此总体能够在 O(q log q) 时间内处理完毕,并在每次操作后常数时间地输出当前不同元素的种类数。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

struct Seg {int r; int v;};                 // 颜色段:终点、颜色
map<int, Seg> seg;                          // key = 段起点
unordered_map<ll,ll> cnt;                   // 颜色 -> 数量
int kinds = 1;                              // 目前不同元素种类数(含 0)
const int N = 100000;                       // 题目保证 r ≤ 1e5

// 在 pos 处断开颜色段并返回右段迭代器
auto split(int pos){
    auto it = seg.lower_bound(pos);
    if (it != seg.end() && it->first == pos) return it;
    --it;                                   // now it->first < pos ≤ it->second.r
    int l = it->first, r = it->second.r, v = it->second.v;
    if (pos > r) return seg.end();
    it->second.r = pos - 1;                 // 左段变短
    return seg.insert({pos, {r, v}}).first; // 插入右段
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int q;  if(!(cin >> q)) return 0;

    seg[1] = {N, 0};                        // 初始整段为 0
    cnt[0] = N;                             // 0 在 [1,N] 内出现 N 次

    while (q--){
        int l, r, x; cin >> l >> r >> x;
        auto itR = split(r + 1);
        auto itL = split(l);

        // 删除被覆盖的旧段
        for (auto it = itL; it != itR; ){
            int len = it->second.r - it->first + 1;
            int v   = it->second.v;
            if (v != 0){
                cnt[v] -= len;
                if (cnt[v] == 0) kinds--;
            }
            it = seg.erase(it);
        }

        // 插入新区间
        seg[l] = {r, x};
        if (x != 0){
            if (cnt[x] == 0) kinds++;
            cnt[x] += (r - l + 1);
        }
        cout << kinds << '\n';
    }
    return 0;
}

全部评论

相关推荐

不愿透露姓名的神秘牛友
07-02 15:39
希望奇迹发生的布莱克...:真的是 现在卷实习就是没苦硬吃
点赞 评论 收藏
分享
07-04 09:21
已编辑
Java
推拿大师:这是hr发的钓鱼贴吗
投递字节跳动等公司8个岗位
点赞 评论 收藏
分享
自学java狠狠赚一...:骗你点star的,港卵公司,记得把star收回去
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务