NKOJ 4254 区间MEX (线段树)

该博客介绍了一种使用线段树解决区间最小排除值(MEX)问题的方法,通过处理数列和区间修改,实现了在给定限制条件下高效地回答区间MEX查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

P4254区间MEX

问题描述

给你一个长度为n的数列,元素编号1到n,第i个元素值为Ai。现在有m个形如(L,R)的提问,你需要回答出区间[L,R]的mex值。即求出区间[L,R]中没有出现过的最小的非负整数。

输入格式

第一行,两个整数n和m
第二行,n个空格间隔的整数,表示数列A
接下来m行,每行两个整数L,R,表示一次询问

输出格式

m行,每行一个整数,表示对应询问的答案。

样例输入

7 5
0 2 1 0 1 3 2
1 3
2 3
1 4
3 6
2 7

样例输出

3
0
3
2
4

提示

1<=n,m<=200000
0<=Ai<=200000
1<=L<=R<=n


由于没有修改,可以考虑离线算法。先将询问按照左端点排序。

S[i]表示区间[1,i]MEX,容易发现S[i]单调不降,并且可以O(n)的处理出来S数组。那么左端点为1的询问都可以处理,然后考虑如何处理左端点为2时

考虑删掉A[1]S数组的影响,那么令x=A[1],那么S[x]以后肯定不会受到影响,而对于x之前的S[k]

如果S[k]>A[i],那么S[k]=A[i],否则不变

那么区间修改用线段树来维护,按照左端点升序讨论即可。


代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#define N 200005
#define M 2000005
#define min(a,b) ((a>b)?(b):(a))
using namespace std;
struct node{int id,l,r,ans;}Q[N];
bool cmp(node a,node b)
{return a.l<b.l;}
bool ccp(node a,node b)
{return a.id<b.id;}
int n,m,A[N],B[N],NE[N],LA[N];
bool mark[N];
int ls[M],rs[M],v[M],lazy[M],tot,rt;
int BT(int x,int y)
{
    int p=++tot;
    lazy[p]=-1;
    if(x<y)
    {
        int mid=x+y>>1;
        ls[p]=BT(x,mid);
        rs[p]=BT(mid+1,y);
    }
    else v[p]=B[x];
    return p;
}
void PD(int p)
{
    int l=ls[p],r=rs[p],d=lazy[p];
    lazy[p]=-1;
    v[l]=min(v[l],d);
    v[r]=min(v[r],d);
    if(lazy[l]==-1)lazy[l]=d;
    else lazy[l]=min(lazy[l],d);
    if(lazy[r]==-1)lazy[r]=d;
    else lazy[r]=min(lazy[r],d);
}
void MD(int p,int l,int r,int x,int y,int d)
{
    if(lazy[p]!=-1)PD(p);
    if(x<=l&&y>=r){lazy[p]=d;v[p]=min(v[p],d);return;}
    int mid=l+r>>1;
    if(x<=mid&&y>=l)MD(ls[p],l,mid,x,y,d);
    if(x<=r&&y>mid)MD(rs[p],mid+1,r,x,y,d);
}
int GA(int p,int l,int r,int k)
{
    if(lazy[p]!=-1)PD(p);
    if(l==r)return v[p];
    int mid=l+r>>1;
    if(k<=mid)return GA(ls[p],l,mid,k);
    return GA(rs[p],mid+1,r,k);
}
int main()
{
    int i,x,y;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)scanf("%d",&A[i]);
    for(i=1;i<=m;i++)scanf("%d%d",&Q[i].l,&Q[i].r),Q[i].id=i;
    sort(Q+1,Q+m+1,cmp);x=0;
    for(i=1;i<=n;i++)
    {
        mark[A[i]]=1;
        while(mark[x])x++;
        B[i]=x;
    }
    for(i=n;i>=1;i--)
    {
        if(!LA[A[i]])NE[i]=n+1;
        else NE[i]=LA[A[i]];
        LA[A[i]]=i;
    }
    rt=BT(1,n);x=1;i=1;
    while(i<=m)
    {
        while(x<Q[i].l)
        {
            if(x+1<NE[x])MD(rt,1,n,x+1,NE[x]-1,A[x]);
            x++;
        }
        while(x==Q[i].l)
        {
            Q[i].ans=GA(rt,1,n,Q[i].r);
            i++;
        }
    }
    sort(Q+1,Q+m+1,ccp);
    for(i=1;i<=m;i++)printf("%d\n",Q[i].ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值