hdu 6730 线段树 2019-ccpc网络赛02
题意:
数组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; }