您的位置:首页 > 其它

[bzoj3339]mex(线段树)

2017-10-25 20:26 141 查看

题目:

我是超链接

题解:

天然巨坑啊

线段树维护区间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
;
int minn[N*4],sg
,a
,last
,nxt
,ans
;
bool vis
;
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]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: