By Xhr
题解:线段树
1-n 扫一遍,计算出1-i的sg值,离线所有询问,按左端点排序,预处理出每个位置的数字下一次出现的位置next。
然后左指针不断后移,从区间l-r 到区间 l+1-r ,原本sg值大于a[l]的现在都可以变成a[l],用线段树维护区间sg,每次修改l-next[a[l]]-1 的值,把a[l]的值赋给区间,然后每个区间内不断下放。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define N 200003 #define inf 1000000000 using namespace std; int n,m; int tr[N*4],b[N],mark[N],sg[N],next[N],point[N],ans[N]; int pd[N]; struct data { int l,r,num; };data a[N]; void build(int now,int l,int r) { tr[now]=inf; if (l==r) { tr[now]=sg[l]; pd[l]=now; return; } int mid=(l+r)/2; build(now<<1,l,mid); build(now<<1|1,mid+1,r); } void pushdown(int x) { tr[x<<1]=min(tr[x],tr[x<<1]); tr[x<<1|1]=min(tr[x],tr[x<<1|1]); } void change(int now,int l,int r,int ll,int rr,int x) { if (tr[now]!=inf&&l!=r) pushdown(now); if (l>=ll&&r<=rr) { tr[now]=min(tr[now],x); return; } int mid=(l+r)/2; if (ll<=mid) change(now<<1,l,mid,ll,rr,x); if (rr>mid) change(now<<1|1,mid+1,r,ll,rr,x); } int cmp(data a,data b) { return a.l<b.l; } int ask(int now,int l,int r,int x) { if (tr[now]!=inf&&l!=r) pushdown(now); if (l==r) return tr[now]; int mid=(l+r)/2; if (x<=mid) return ask(now<<1,l,mid,x); else return ask(now<<1|1,mid+1,r,x); } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&b[i]); int k=0; for (int i=1;i<=n;i++) { mark[b[i]]=1; if (b[i]==k) while (mark[k]) k++; sg[i]=k; } for (int i=1;i<=n;i++) point[b[i]]=n+1; for (int i=n;i>=1;i--) { next[i]=point[b[i]]; point[b[i]]=i; } for (int i=1;i<=m;i++) scanf("%d%d",&a[i].l,&a[i].r),a[i].num=i; sort(a+1,a+m+1,cmp); build(1,1,n); int l=1; for (int i=1;i<=m;i++) { while (l<a[i].l) { int r; r=next[l]-1; change(1,1,n,l,r,b[l]); l++; } ans[a[i].num]=ask(1,1,n,a[i].r); } for (int i=1;i<=m;i++) printf("%d\n",ans[i]); }