[bzoj3339]mex(线段树)

题目:

我是超链接

题解:

天然巨坑啊
线段树维护区间mex值!
在线的话并不怎么会,那可以试试离线啊,把所有询问离线下来按照左端点排序
然后我们考虑l~r和l+1~r的区别,就是l~next[l]-1区间内所有mex比a[l]大的全部改成a[l]
如果把修改的操作想出线段树的话就很简单了吧
要先求出1~i的mex值哦

线段树的叶子节点表示[now,i]这个区间(now是现在左端点的位置,i是查询到的右端点)
线段树中的非叶子节点并没有实际含义,只是为了修改操作的方便。

这种非叶节点没有意义的方法也要学习!

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=200005;
struct hh{int l,r,id;}q[N];
int minn[N*4],sg[N],a[N],last[N],nxt[N],ans[N];
bool vis[N];
int cmp(hh a,hh b){if (a.l==b.l) return a.r<b.r;else return a.l<b.l;}
void build(int now,int l,int r)
{
    minn[now]=N;
    if (l==r){minn[now]=sg[l];return;}
    int mid=(l+r)>>1;
    if (l<=mid) build(now<<1,l,mid);
    if (r>mid) build(now<<1|1,mid+1,r);
}
void pushdown(int now)
{
    minn[now<<1]=min(minn[now<<1],minn[now]);
    minn[now<<1|1]=min(minn[now<<1|1],minn[now]);
}
int ask(int now,int l,int r,int k)
{
    if (minn[now]!=N) pushdown(now);
    if (l==r) return minn[now];
    int mid=(l+r)>>1;
    if (k<=mid) return ask(now<<1,l,mid,k);
    else return ask(now<<1|1,mid+1,r,k);
}
void change(int now,int l,int r,int lrange,int rrange,int k)
{
    if (lrange<=l && rrange>=r) {minn[now]=min(minn[now],k); return;}
    int mid=(l+r)>>1;
    if (lrange<=mid) change(now<<1,l,mid,lrange,rrange,k);
    if (rrange>mid) change(now<<1|1,mid+1,r,lrange,rrange,k);
}
int main()
{
    int n,Q,i;
    scanf("%d%d",&n,&Q);
    for (i=1;i<=n;i++) scanf("%d",&a[i]),last[a[i]]=n+1;
    int mq=0;
    for (i=1;i<=n;i++)
    {
        vis[a[i]]=1;
        while (vis[mq]) mq++;
        sg[i]=mq;
    }
    for (i=n;i>=1;i--) nxt[i]=last[a[i]],last[a[i]]=i;
    build(1,1,n);
    for (i=1;i<=Q;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
    sort(q+1,q+Q+1,cmp);
    int now=1;
    for (i=1;i<=Q;i++)
    {
        while (now<q[i].l)
        {
            change(1,1,n,now,nxt[now]-1,a[now]);//比a[now]大的全都改成a[now] 
            now++;
        }
        ans[q[i].id]=ask(1,1,n,q[i].r);
    }
    for (i=1;i<=Q;i++) printf("%d\n",ans[i]);
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值