array
题意
给出一个1,n的排列,有q(q<=1e5)有以下两个操作(强制在线):
1.给位置pos上的数+1e7
2.询问[1,r]区间上大于等于k(1e5)的数最小是多少(这个数不能等于[1,r]区间中的任意一个数)
分析
由数据我们可以知道只要对一个数进行了操作1,相当于把这个数挖掉了,所以我们想要找到最小的满足题意的值,只需要维护区间最大值,只要区间中有值大于等于k,就可以进区间找,但是这样会带来很多问题,首先是区间全是连续的无法插,其次是可以插,如何找到这个空?这个时候需要转化思维,既然普通线段树难以维护,是否考虑主席树or权值线段树,使用权值线段树,维护区间最大的标号,如果标号大于等于r,先往左边找,但是这样可能会导致[1,mid]中有标号比r大的,但是比k小,这样就会一直找到mid,只需要开一个变量记一下,如果[1,mid]不合法继续到右子树找就行了(比赛的时候心态崩了这么简单处理都不会了,要是冷静下来改改就过了)
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+4; const int inf=0x3f3f3f3f; int tr[maxn<<2]; int a[maxn],b[maxn],vis[maxn]; void build(int o,int l,int r){ if(l==r)tr[o]=b[l]; else { int mid=l+r>>1; build(o<<1,l,mid); build(o<<1|1,mid+1,r); tr[o]=max(tr[o<<1],tr[o<<1|1]); } } void update(int o,int l,int r,int p){ if(l==r)tr[o]=inf-1; else { int mid=l+r>>1; if(p<=mid)update(o<<1,l,mid,p); else update(o<<1|1,mid+1,r,p); tr[o]=max(tr[o<<1],tr[o<<1|1]); } } int ok=0; int query(int o,int l,int r,int x,int y,int k){ //cout<<l<<" "<<r<<endl; if(l==r){ //cout<<tr[o]<<endl; if(tr[o]<=y){ return inf; } ok=1; return l; } else { int mid=l+r>>1; int ans=inf; if(k<=mid&&tr[o<<1]>y){ ans=query(o<<1,l,mid,x,y,k); if(ans!=inf)return ans; else if(ok==0){ return query(o<<1|1,mid+1,r,x,y,k); } } else { return query(o<<1|1,mid+1,r,x,y,k); } } } int main(){ int t; scanf("%d",&t); while(t--){ int n,q; scanf("%d%d",&n,&q); for(int i=1;i<=n;i++)scanf("%d",&a[i]); int sz=n+2; for(int i=1;i<=n+2;i++){ b[i]=inf-1; } for(int i=1;i<=n;i++)vis[i]=0; for(int i=1;i<=n;i++)b[a[i]]=i; build(1,1,sz); int pos,r,k; int ans=0; while(q--){ int op; ok=0; scanf("%d",&op); if(op==1){ scanf("%d",&pos); pos=pos^ans; if(vis[pos]==0)update(1,1,sz,a[pos]),vis[pos]=1; } else { scanf("%d%d",&r,&k); r=r^ans; k=k^ans; ans=query(1,1,sz,1,r,k); printf("%d\n",ans); } } } return 0; }