H. Excluded Min (思维、线段树)

73 篇文章 0 订阅

题目

https://codeforces.com/gym/103260/problem/H
在这里插入图片描述

思路

在这里插入图片描述
写了一坨答辩代码,已经神志不清了,后面再补上思路吧,先给上题解的描述还有草稿纸上一些图。
简而言之,学会了一种套路,求一些线段中选出不被任何一个线段完全覆盖的所有线段,支持删除线段操作。
在这里插入图片描述

代码

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

#define MAXN 500005

int n, q;
int a[MAXN];
pair<pair<int, int>, int> Q[MAXN];


#define LX (x<<1)
#define RX ((x<<1)|1)


struct segment_tree {
    struct node {
        int l, r;
        int v;      // 区间最大值
        int num;    // 区间线段个数
        int tag;    // 区间加标记
    } a[MAXN*8];

    void merge(int x) {
        if (a[x].l == a[x].r) return;
        a[x].num = a[LX].num + a[RX].num;
        a[x].v = max(a[LX].v, a[RX].v);
    }

    void build(int x, int l, int r) {
        a[x].l = l;
        a[x].r = r;
        a[x].num = a[x].tag = 0;
        a[x].v = -1;
        if (l == r) return;
        build(LX, l, (l+r)/2);
        build(RX, (l+r)/2+1, r);
        merge(x);
    }

    void down_tag(int x) {
        if (a[x].l == a[x].r) return;
        if (a[LX].num) a[LX].v += a[x].tag, a[LX].tag += a[x].tag;
        if (a[RX].num) a[RX].v += a[x].tag, a[RX].tag += a[x].tag;
        a[x].tag = 0;
    }

    void set(int x, int pos, int v) {       // 将线段 pos 处的值设置为 v,若 v==-1 则说明清除这个点
        if (a[x].l == a[x].r) {
            a[x].v = v;
            a[x].num = (v>=0);
            return;
        }
        down_tag(x);
        if (a[LX].r >= pos) set(LX, pos, v);
        else set(RX, pos, v);
        merge(x);
    }

    void add(int x, int L, int R, int d) {  // 给区间内存在的线段 +d

        if (!a[x].num) return;

        if (L<=a[x].l && a[x].r<=R) {
            a[x].tag += d;
            a[x].v += d;
            return;
        }

        down_tag(x);
        
        if (a[LX].r>=L) add(LX, L, R, d);
        if (a[RX].l<=R) add(RX, L, R, d);

        merge(x);
    }

    int query(int x, int pos) {         // 查询 pos 处的值
        if (a[x].l == a[x].r) {
            return a[x].v;
        }

        down_tag(x);

        if (a[LX].r >= pos) return query(LX, pos);
        return query(RX, pos);
    }

    int mxv(int x) {                    // 查询整个线段的最大值,若线段为空则返回-1
        return a[x].num ? a[x].v : -1;
    }

    int id_mxv(int x) {                 // 返回最大值所在的下标(左端点)(有多个则随便一个)
        if (a[x].l == a[x].r) {
            return a[x].l;
        }
        down_tag(x);
        return (a[LX].v == a[x].v) ? id_mxv(LX) : id_mxv(RX);
    }

} A;




struct segment_tree1 {
    struct node {
        int l, r;
        pair<int, int> v;   // <maxr, qid>
    } a[MAXN*8];

    void build(int x, int l, int r) {
        a[x].l = l;
        a[x].r = r;
        if (l == r) return;
        build(LX, l, (l+r)/2);
        build(RX, (l+r)/2+1, r);
    }

    void set(int x, int pos, pair<int, int> v) {
        if (a[x].l == a[x].r) {
            a[x].v = v;
            return;
        }
        if (a[LX].r >= pos) set(LX, pos, v);
        else set(RX, pos, v);

        a[x].v = (a[RX].v.first > a[LX].v.first) ? a[RX].v : a[LX].v;   // maxr 相同,取左边的
    }

    pair<int, int> query(int x, int L, int R) {
        if (L<=a[x].l && a[x].r<=R) {
            return a[x].v;
        }

        pair<int, int> lv, rv;
        if (a[LX].r >= L) lv = query(LX, L, R);
        if (a[RX].l <= R) rv = query(RX, L, R);
        return (rv.first>lv.first) ? rv : lv;
    }

} B;



struct BIT {
    int c[MAXN];

    int lowbit(int x) {return x&(-x);}

    void add(int x, int v) {
        for (; x<=n; x+=lowbit(x)) c[x]+=v;
    }
    int query(int x) {
        int ret=0;
        for (; x>0; x-=lowbit(x)) ret+=c[x];
        return ret;
    }
    int query(int l, int r) {
        return query(r)-query(l-1);
    }
} C;



vector<int> pos_val[MAXN];          // pos_val[v] = {i | a[i]==v}
vector<int> qid_l[MAXN];
int active_qid_l[MAXN];
int ans[MAXN];
set<pair<int, int>> active_l;       // <l, qid>
set<pair<int, int>> active_r;       // <r, qid>


void add_qseg(int ql) {
    int qid = qid_l[ql].back();
    int qr = Q[qid].first.second;

    active_l.insert({ql, qid});
    active_r.insert({qr, qid});
    active_qid_l[ql] = qid;

    int tmp = C.query(ql, qr);
    A.set(1, ql, tmp);

    qid_l[ql].pop_back();
    if (qid_l[ql].size()) {
        B.set(1, ql, {Q[qid_l[ql].back()].first.second, qid_l[ql].back()});
    }
    else {
        B.set(1, ql, {0, 0});
    }
}

void del_qseg(int ql) {
    int qid = active_qid_l[ql];
    int qr = Q[qid].first.second;

    active_l.erase({ql, qid});
    active_r.erase({qr, qid});

    A.set(1, ql, -1);
}

void activate_qseg(int tmpl, int tmpr, int minr) {
    while (tmpr>=tmpl) {
        auto [_, qid] = B.query(1, tmpl, tmpr);

        if (!qid || Q[qid].first.second < minr) break;

        add_qseg(Q[qid].first.first);

        tmpr = Q[qid].first.first-1;
    }
}


void solve()
{
    scanf("%d%d", &n, &q);
    for (int i=1; i<=n; i++) {
        scanf("%d", &a[i]);
        pos_val[a[i]].push_back(i);
    }
    for (int i=1; i<=q; i++) {
        Q[i].second = i;
        scanf("%d%d", &Q[i].first.first, &Q[i].first.second);
    }

    A.build(1, 1, n);

    B.build(1, 1, n);

    for (int i=1; i<=n; i++) C.add(i, 1);

    sort(Q+1, Q+q+1);

    for (int i=1; i<=q; i++) qid_l[Q[i].first.first].push_back(i);
    for (int i=1; i<=q; i++) if (Q[i].first.first != Q[i-1].first.first) {
        int ql = Q[i].first.first;
        int qid = qid_l[ql].back();
        int qr = Q[qid].first.second;
        B.set(1, ql, {qr, qid});
    }

    
    activate_qseg(1, n, 0);


    for (int x=n; x>=0; x--) {

        for (int pos: pos_val[x]) {
            auto p_l = active_r.lower_bound({pos, 0});
            auto p_r = active_l.upper_bound({pos, n});
            int L=n+1, R=0;
            if (p_l != active_r.end()) {
                L = Q[p_l->second].first.first;
            }
            if (p_r != active_l.begin()) {
                p_r--;
                R = p_r->first;
            }
            
            if (L <= R) {
                A.add(1, L, R, -1);
            }
            C.add(pos, -1);
        }

        while (A.mxv(1) >= x) {
            int cur_l = A.id_mxv(1);
            int cur_id = active_qid_l[cur_l];
            int cur_r = Q[cur_id].first.second;
            ans[Q[cur_id].second] = x;

            int tmpl = cur_l, tmpr = cur_r, minr = 0;
            auto p_nxt = ++active_l.find({cur_l, cur_id});
            if (p_nxt != active_l.end()) {
                tmpr = min(tmpr, Q[p_nxt->second].first.first - 1);
            }

            auto p_lst = active_r.find({cur_r, cur_id});
            if (p_lst != active_r.begin()) {
                p_lst--;
                minr = Q[p_lst->second].first.second + 1;
            }

            del_qseg(cur_l);

            activate_qseg(tmpl, tmpr, minr);
        }
    }

    for (int i=1; i<=q; i++) {
        printf("%d\n", ans[i]);
    }

}



int main()
{
    solve();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值