数组a时一个1~n的全排列,有如下两种操作
1 x,将a[x]+=1e7
2 r,k 查询大于等于k的最小值但不在数组中前r项中
(数组长度1e5,k<1e5)
首先观察可知在题目中查询的答案一定再1~n+1
的范围内(因为k很小,操作一加的很大)
那么其实1操作就是取消掉禁用。之后考虑正着做比较难,则反着做查询k~n+1
中最小的没被禁用的。
这里用一颗线段树就表示num[i]是否被禁用,查询的时候查询k~n+1
中最小的没被ban掉的就好了,这里左右子树选择的时候再判断一下左子树上k~mid
的最值就好。左右判断一个log,查询本身一个log。最终nlogn^2通过此题.
#include <bits/stdc++.h>
#define LL long long
#define pii pair<int,int>
#define PB push_back
#define X first
#define Y second
using namespace std;
int t,n,m,x;
const int maxn = 5e5;
const int inf = 0x3f3f3f3f;
int a[maxn],b[maxn];
int rmin[maxn];
int ans,op,pos,minx,k,r;
void init(){
memset(rmin,0,sizeof(rmin));
}
void update(int x,int y,int L,int R,int rt){
if(L==R){
rmin[rt]=y;
return;
}
int mid = (L+R)/2;
if(x<=mid) update(x,y,L,mid,rt*2);
else update(x,y,mid+1,R,rt*2+1);
rmin[rt]=max(rmin[rt*2],rmin[rt*2+1]);
}
int get_min(int L,int R,int l,int r,int rt){
if(L>=l&&R<=r) return rmin[rt];
if(r<L||l>R) return 0;
int mid = (L+R)/2;
return max(get_min(L,mid,l,r,rt*2),get_min(mid+1,R,l,r,rt*2+1));
}
void query(int L,int R,int r,int k,int rt){
if(L==R){
ans = L;
return;
}
int mid = (L+R)/2;
if(k<=mid&&rmin[rt*2]>r&&get_min(L,mid,k,mid,rt*2)>r) query(L,mid,r,k,rt*2);
else query(mid+1,R,r,k,rt*2+1);
}
int main(){
//cin>>t;
scanf("%d",&t);
while(t--){
//cin>>n>>m;
scanf("%d%d",&n,&m);
init();
ans=0;
for(int i=1;i<=n;i++){
//cin>>x;
scanf("%d",&x);
a[x]=i;
b[i]=x;
update(x,i,1,n+1,1);
}
update(n+1,inf,1,n+1,1);
for(int i=0;i<m;i++){
scanf("%d",&op);
//cin>>op;
if(op==1){
scanf("%d",&pos);
//cin>>pos;
pos^=ans;
update(b[pos],inf,1,n+1,1);
}
else{
scanf("%d%d",&r,&k);
//cin>>r>>k;
r^=ans;k^=ans;
//cout<<"r:"<<r<<" k:"<<k<<endl;
ans = minx = inf;
query(1,n+1,r,k,1);
printf("%d\n",ans);
//cout<<ans<<endl;
}
}
}
return 0;
}
原文:https://www.cnblogs.com/zhangxianlong/p/11432003.html