线段树-Mex-洛谷P4137

Mex

问题提出

有一个长度为 n n n的数组 { a 1 , a 2 , … , a n } \{a_1,a_2,…,a_n\} {a1,a2,,an} m m m次询问,每次询问一个区间内最小没有出现过的自然数。

题目解答

1 − n 1-n 1n这里能够的每个数 x x x,都统计出来在数组中出现的位置,并在前补上 0 0 0,在后补上 n + 1 n+1 n+1.

例如数组 { 1 , 2 , 3 , 2 , 1 } \{1,2,3,2,1\} {1,2,3,2,1},
其中 1 1 1出现过的位置就是: { 0 , 1 , 5 , 6 } \{0,1,5,6\} {0,1,5,6}
其中 2 2 2出现过的位置就是: { 0 , 2 , 4 , 6 } \{0,2,4,6\} {0,2,4,6}
其中 3 3 3出现过的位置就是: { 0 , 3 , 6 } \{0,3,6\} {0,3,6}
其中 4 4 4出现过的位置就是: { 0 , 6 } \{0,6\} {0,6}
其中 5 5 5出现过的位置就是: { 0 , 6 } \{0,6\} {0,6}

如果一个自然数 x x x没有在一个区间 [ l , r ] [l,r] [l,r]中出现过,那么必然存在 x x x的出现序列中的两个位置 p 1 , p 2 p_1,p_2 p1,p2满足 p 1 &lt; l &lt; r &lt; p 2 p_1&lt;l&lt;r&lt;p_2 p1<l<r<p2.

那么这就变成一个经典的 2 2 2维数点问题啦!

我们把每个 x x x的出现位置序列,相邻两个数并成一个二维数点,插入到线段树中去.

对于每个询问 ( l , r ) (l,r) (l,r)在线段树中找满足 p 1 &lt; l &lt; r &lt; p 2 p_1&lt;l&lt;r&lt;p_2 p1<l<r<p2条件的权值最小的点就可以了.

将所有的询问 ( l , r ) (l,r) (l,r)和数点 p 1 , p 2 p_1,p_2 p1,p2按照第二维排序,从左往右枚举,遇到询问就回答,遇到数点就插入.

排序可以省掉一维.

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <map>
#define pr(x) std::cout << #x << ':' << x << std::endl
#define rep(i,a,b) for(int i = a;i <= b;++i)
#define repi(i,a,b) for(int i = a;i >= b;--i)
#define clr(x) memset(x,0,sizeof(x))
#define setinf(x) memset(x,0x3f,sizeof(x))

const int N = 200010;
const int inf = 1e9+7;
int n,m;
int a[N];
std::map<int,int> map;

struct node{
    int num;
    node(int x = inf){num = x;}
}ns[N<<2];

struct que{
    int l,r,id;
    que(int l = 0,int r = 0,int id = 0) {this->l = l,this->r = r,this->id = id;}
    bool operator<(const que &q)const{
        return l < q.l;
    }
}qs[N],qs2[N<<2];

int ans[N];

node maintain(node &lch,node &rch) {
    node res = node();
    res.num = std::min(lch.num,rch.num);
    return res;
}	

void change(int o,int l,int r,int pos,int val) {
    if(l == r) {
        if(val < ns[o].num) 
            ns[o].num = val;
        return ;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) 
        change(o<<1,l,mid,pos,val);
    else 
        change(o<<1|1,mid+1,r,pos,val);
    
    ns[o] = maintain(ns[o<<1],ns[o<<1|1]);
}

node query(int o,int l,int r,int ql,int qr) {
    if(ql <= l && r <= qr) 
        return ns[o];
    if(r < ql || qr < l)
        return node();	
    int mid = (l + r) >> 1;
    node lch = query(o<<1,l,mid,ql,qr);
    node rch = query(o<<1|1,mid+1,r,ql,qr);
    return maintain(lch,rch);
}

void build(int o,int l,int r) {
    if(l == r) {
        ns[o] = node();
        return ;
    }
    int mid = (l + r) >> 1;
    build(o<<1,l,mid);
    build(o<<1|1,mid+1,r);
    ns[o] = maintain(ns[o<<1],ns[o<<1|1]);
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin >> n >> m;
    build(1,0,n+1);
    rep(i,1,n) std::cin >> a[i];
    int cc = 0;
    rep(i,1,n){
        qs2[cc++] = que(map[a[i]],i,a[i]);
        map[a[i]] = i;
    }
    rep(i,0,2*n) {
        qs2[cc++] = que(map[i],n+1,i);
    }
    std::sort(qs2,qs2+cc);
    rep(i,1,m) {
        std::cin >> qs[i].l >> qs[i].r;
        qs[i].id = i;
    }	
    std::sort(qs+1,qs+1+m);
    int pos = 0;
    rep(i,1,m) {
        while(pos < cc && qs2[pos].l < qs[i].l){
            change(1,0,n+1,qs2[pos].r,qs2[pos].id);
            ++pos;
        }
        node res = query(1,0,n+1,qs[i].r+1,n+1);
        ans[qs[i].id] = res.num;
    }
    rep(i,1,m) {
        std::cout << ans[i] << std::endl;
    }
    
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值