[bzoj3339]mex(线段树)

来源:互联网 发布:cyberduck mac版 编辑:程序博客网 时间:2024/06/06 07:21

题目:

我是超链接

题解:

天然巨坑啊
线段树维护区间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]);}
原创粉丝点击