NKOI 2691 MEX ll

来源:互联网 发布:淘宝女童丝袜模特 编辑:程序博客网 时间:2024/05/29 16:47

【冬季集训】Mex II

Time Limit:20000MS  Memory Limit:123456K
Total Submit:64 Accepted:19
Case Time Limit:1000MS

Description

在SG定理中,对于一个由自然数组成的有限集合S,mex{S}定义为不在集合S中的最小自然数,例如mex{0,1,2}=3,mex{2,3,5}=0.
给你一个长度为N的非负整数序列{A1,A2, ... ,AN},定义mex(l,r)=mex{Al,Al+1, ... ,Ar}.
现有Q个询问,每次提问mex(l,r)的值.

Input

第一行两整数N,Q,
第二行N个整数,第i个数为Ai
接下来Q行每行两个数为一次提问。

Output

对每次提问输出一行,为这次提问的答案。

Sample Input

7 50 2 1 0 1 3 21 32 31 43 62 7

Sample Output

30324

Hint

1<=N<=200000
1<=Q<=200000
0<=Ai<=200000

Source

BZOJ 3339


这道题是一道异常恶心的题,据说可以用线段树来优化,但是能力有限,写了个不算暴力的暴力,然后加了个输入优化,勉勉强强能A

这道题类似于博客中前一道题HH的项链,都要用到离线操作且方式基本类似

我们要首先预处理两个东西,一个next[i]和一个mex[i]数组,其中mex[i]表示序列A中第1个数到第i个数的mex值,这个东西用小根堆可以轻松搞定,具体看下面代码

然后next数组就类似于链表,next[i]记录的是与a[i]数字相同的数字的下一个坐标

然后我们设mex(a,b)表示a,b区间的mex值,由于我们知道了mex(1,b),那么只要扔掉第一个元素我就可以知道mex(2,b),并以此类推

然后难点就难在怎么求扔了一个元素后某一个区间的mex

我们要注意到mex的值是单调不降的,我们要利用好这个性质。
考虑将mex(k,k),mex(k,k+1),mex(k,k+2),.......,mex(k,N)
这么一个序列通过什么操作可以变成mex(k+1,k+1),mex(k+1,k+2),mex(k+1,k+3),......,mex(k+1,N)这个序列。
我们发现,去掉A[k]之后,从k+1到next[k]-1之间的所有大于A[k]的数都变成了A[k].
由于mex序列的单调不降的性质,我们只需要将一段区间的mex全部改成A[k]就行了。

然后很明显这里可以用线段树维护mex,但是太复杂,而且本人能力有限,就写了个类暴力,运气比较好还能过

#include<cstdio>#include<iostream>#include<vector>#include<queue>#include<algorithm>using namespace std;const int inf=1e9;const int maxn=200005;int s[maxn],n,m,a,b;int mex[maxn],NEXT[maxn],head[maxn];bool mark[maxn];priority_queue<int ,vector<int>,greater<int> >q;struct node{int l,r,id,ans;}w[maxn];bool cmp1(node a,node b){return a.l==b.l?a.r<b.r:a.l<b.l;}bool cmp2(node a,node b){return a.id<b.id;}inline void _read(int &x){    char t=getchar();bool sign=true;    while(t<'0'||t>'9')    {if(t=='-')sign=false;t=getchar();}    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';    if(!sign)x=-x;}int main(){    _read(n);_read(m);    int i,j,l=1;    for(i=1;i<=n;i++){        _read(s[i]);        if(!mark[s[i]]){            q.push(s[i]);            mark[s[i]]=1;        }        mex[i]=mex[i-1];        while(q.size()&&q.top()==mex[i]){            mex[i]++;            q.pop();        }    }//边输入边用小根堆求mex数组    for(i=n;i;i--)        NEXT[i]=head[s[i]],head[s[i]]=i;//next数组求法    for(i=1;i<=m;i++){        _read(w[i].l);_read(w[i].r);        w[i].id=i;    }    sort(w+1,w+1+m,cmp1);        for(i=1;i<=m;i++){        while(l<w[i].l){        if(NEXT[l]==0)NEXT[l]=n+1;int pos=lower_bound(mex+1+l,mex+NEXT[l],s[l])-mex;        if(pos<s[l])pos=inf;        for(j=pos;j<=min(NEXT[l]-1,n);j++)mex[j]=s[l];        l++;        }        w[i].ans=mex[w[i].r];    }//这里的变量l作用与HH的项链一题类似,可以参考上一篇博客    sort(w+1,w+1+m,cmp2);    for(i=1;i<=m;i++)        printf("%d\n",w[i].ans);} 

然后经过研究,我套了个线段树的模板上去,修修改改居然过了,而且比上面的暴力快了很多,A题肯定就没有问题了,下面附上代码

#include<cstdio>#include<iostream>#include<queue>#include<vector>#include<algorithm>using namespace std;const int maxn=200005;const int inf=1e9;priority_queue<int,vector<int>,greater<int> >q;int n,m,s[maxn],mex[maxn],head[maxn],NEXT[maxn],tot,a,b;bool mark[maxn];struct wk{int l,r,ans,id;}qest[maxn];struct wr{int a,b,left,right,minn,maxx,lazy;}tree[maxn<<2];inline void _read(int &x){    char ch=getchar(); bool mark=false;    for(;!isdigit(ch);ch=getchar())if(ch=='-')mark=true;    for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';    if(mark)x=-x;}bool cmp1(wk a,wk b){return a.l==b.l?a.r<b.r:a.l<b.l;}bool cmp2(wk a,wk b){return a.id<b.id;}void build(int x,int y){int r=++tot;tree[r].a=x,tree[r].b=y;tree[r].lazy=-1;if(x<y){int mid=(x+y)>>1;tree[r].left=tot+1;build(x,mid);tree[r].right=tot+1;build(mid+1,y);tree[r].maxx=max(tree[tree[r].left].maxx,tree[tree[r].right].maxx);tree[r].minn=min(tree[tree[r].left].minn,tree[tree[r].right].minn);}else tree[r].minn=tree[r].maxx=mex[x];}void pushdown(int r){int ls=tree[r].left,rs=tree[r].right;tree[ls].lazy=tree[rs].lazy=tree[r].lazy;tree[ls].minn=tree[rs].minn=tree[r].lazy;tree[ls].maxx=tree[rs].maxx=tree[r].lazy;tree[r].lazy=-1;}void change(int r,int d){if(tree[r].lazy>=0)pushdown(r);if(a<=tree[r].a&&b>=tree[r].b){tree[r].lazy=tree[r].minn=tree[r].maxx=d;return ;}int mid=(tree[r].a+tree[r].b)>>1;if(a<=mid)change(tree[r].left,d);if(b>mid)change(tree[r].right,d); tree[r].maxx=max(tree[tree[r].left].maxx,tree[tree[r].right].maxx);tree[r].minn=min(tree[tree[r].left].minn,tree[tree[r].right].minn);}int ask(int r,int d){int ls=tree[r].left,rs=tree[r].right;int mid=(tree[r].a+tree[r].b)>>1; if(tree[r].lazy>=0)pushdown(r);if(tree[r].a==tree[r].b)return tree[r].minn;if(d<=mid)return ask(ls,d);else return ask(rs,d);}  int swer(int r,int p){int ls=tree[r].left,rs=tree[r].right;int mid=(tree[r].a+tree[r].b)>>1; if(tree[r].lazy>=0)pushdown(r);if(tree[r].minn>=p&&a<=tree[r].a&&b>=tree[r].b)return tree[r].a;if(a<=mid&&tree[ls].maxx>=p)return swer(ls,p);else if(b>mid&&tree[rs].maxx>=p)return swer(rs,p);else return inf;}int main(){int i;_read(n); _read(m);for(i=1;i<=n;i++){    _read(s[i]);if(!mark[s[i]]){q.push(s[i]);mark[s[i]]=1;}mex[i]=mex[i-1];while(!q.empty()&&mex[i]==q.top()){mex[i]++;q.pop();} }for(i=n;i;i--)NEXT[i]=head[s[i]],head[s[i]]=i;for(i=1;i<=m;i++){_read(qest[i].l);_read(qest[i].r);qest[i].id=i;} sort(qest+1,qest+1+m,cmp1);build(1,n);int l=1;for(i=1;i<=m;i++){while(l<qest[i].l){if(NEXT[l]==0)NEXT[l]=n+1;a=l,b=NEXT[l]-1;int pos=swer(1,s[l]);if(pos&&pos<NEXT[l]){a=pos;change(1,s[l]);}l++;}qest[i].ans=ask(1,qest[i].r);}sort(qest+1,qest+1+m,cmp2);for(i=1;i<=m;i++)    printf("%d\n",qest[i].ans);}


0 0
原创粉丝点击