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);}
- NKOI 2691 MEX ll
- ll
- ll
- ll
- ll
- ll
- ll
- LL
- mex
- Mex
- mex
- mex
- ll(k)
- LL parser
- LL(1)
- ll命令
- ll.bat
- NSGA-ll
- July 14th 模拟赛C T1 输油管道 Solution
- Linux网络编程常用头文件解释
- JSON详解
- 关于yum Error: Cannot retrieve repository metadata (repomd.xml) for repository:xxxxxx.
- Unix系统编程(4) - 多进程并发服务器
- NKOI 2691 MEX ll
- boost::log
- Java中输入一个十进制数,如何转换为二进制数
- Email邮件头揭密
- java web工程中获取webroot 路径
- 【bzoj1566】【NOI2009】【管道取珠】【dp】
- srt字幕解析(上)
- 编程如练功,一日练得一日功,一日不练十日空。
- tarjan算法详解