给定序列,互不相同的全排列 n1e5内的。
1操作使得某个点+1e7
2操作 找一个要求【不等于a1...ar 中的值】【且这个值大于等于k】【值最小】
主席树+set
暴力的想,从小到大取数看看可不可行。又由于整个序列是全排序。
那么不在[1,r]中,就相当于在[r+1,n]中找一个大于等于k。这个可以用主席树写。
然后考虑操作1,这个点+1e7之后这个点原先的值就不会被[1,r]这个限制范围了
比如[4 2 1] 3 5 6 ,先对4这个位置+1e7 然后k=4
对4 +1e7之后,我们发现4就可用了。原先是 4 2 1不可用。现在变成了 4+1e7 2 1不可用。
把操作1之后的数丢进set里。
然后发现不冲突,那么就可以取小得到答案了。
#include<bits/stdc++.h> #define inf 1e18 #define ll long long #define ull unsigned long long #define int long long #define PI acos(-1.0) #define PII pair<int,int> using namespace std; const int N = 1e5+7; const int p = 998244353; int m,n,idx,a[N],root[N]; set<int>s; struct node{ int l,r,cnt; }tr[N << 5]; void build(int l,int r,int &rt){ rt=++idx; if(l==r) return ; int mid=(l+r)>>1; build(l,mid,tr[rt].l);build(mid+1,r,tr[rt].r); } int insert(int p,int l,int r,int x){ int q = ++idx; tr[q]=tr[p];tr[q].cnt++; if(l==r) return q; int mid = l+r >>1; if(x<=mid) tr[q].l=insert(tr[p].l,l,mid,x); else tr[q].r=insert(tr[p].r,mid+1,r,x); return q; } int query(int q,int p,int l,int r,int k){ if(l==r) return l; int mid =l+r>>1; int x1 = tr[tr[q].l].cnt - tr[tr[p].l].cnt; int x2 = tr[tr[q].r].cnt - tr[tr[p].r].cnt; int ans = inf; if(k<=mid&&x1) ans = query(tr[q].l,tr[p].l,l,mid,k); if(ans != inf) return ans; if(x2) ans = query(tr[q].r,tr[p].r,mid+1,r,k); return ans; } void solve(){ s.clear(); scanf("%lld%lld",&n,&m); for(int i=1;i<=n;++i)scanf("%lld",&a[i]); idx = 0; build(1,n+1,root[0]); for(int i=1;i<=n;++i)root[i]=insert(root[i-1],1,n+1,a[i]); root[n+1] = insert(root[n],1,n+1,n+1); int ans = 0; while(m--){ int op;int t1,t2,t3;scanf("%lld",&op); if(op==1){ scanf("%lld",&t1); int pos = ans ^ t1; s.insert(a[pos]); } else{ scanf("%lld%lld",&t2,&t3); int r = t2 ^ ans,k = t3 ^ ans; //printf("r %lld k %lld ",r,k); //[r+1,n+1]找大于等于k的值 ans = query(root[n+1],root[r],1,n+1,k); if(s.size()){ auto it = s.lower_bound(k); if(it!=s.end()) ans = min(ans,*it); } printf("%lld ",ans); } } } signed main(){ int t=1; scanf("%lld",&t); for(int _=1;_<=t;++_){ solve(); } return 0; }
权值线段树
【不等于a1...ar 中的值】 == 【值下标大于r】
【且这个值大于等于k】 == 【查询时在[k,n+1]中查询】
【最小】 == 【能左子树就左,查询时有交集取小】
#include<bits/stdc++.h> #define inf 1e7 #define ll long long #define ull unsigned long long #define PI acos(-1.0) #define PII pair<int,int> using namespace std; const int N = 1e5+7; const int p = 998244353; int n,q; struct node{ int l,r,pos; //维护区间下标最大值 }t[4*N]; int a[N]; void build(int l,int r,int o){ t[o].l=l; t[o].r=r; if(l==r) return ; int mid=(l+r)>>1; build(l,mid,o<<1); build(mid+1,r,o<<1|1); } void pushup(int o){ t[o].pos=max(t[o<<1].pos,t[o<<1|1].pos); } void add(int o,int x,int pos){ //添加操作 维护区间下标最大值 if(t[o].l==t[o].r&&t[o].l==x){ t[o].pos=pos; return ; } int mid=(t[o].l+t[o].r)>>1; if(x<=mid) add(o<<1,x,pos); else add(o<<1|1,x,pos); pushup(o); } int query(int o,int l,int r,int ask){ if(t[o].l==t[o].r){ //到叶子节点 if(t[o].pos>ask) return t[o].l; else return inf; } if(t[o].l==l&&t[o].r==r){//剪枝 if(t[o<<1].pos>ask) return query(o<<1,l,(l+r)>>1,ask); else if(t[o<<1|1].pos>ask) return query(o<<1|1,(l+r)>>1|1,r,ask); else return inf; } int mid=(t[o].l+t[o].r)>>1; if(r<=mid) return query(o<<1,l,r,ask); else if(l>mid) return query(o<<1|1,l,r,ask); else return min(query(o<<1,l,mid,ask),qurey(o<<1|1,mid+1,r,ask)); } void solve(){ memset(t,0,sizeof t); scanf("%d%d",&n,&q); build(1,1+n,1); for(int i=1; i<=n; i++){ scanf("%d",&a[i]); add(1,a[i],i); } add(1,n+1,inf); int op,last=0; while(q--){ scanf("%d",&op); if(op==1){ int pos;scanf("%d",&pos); pos^=last;add(1,a[pos],inf); } else{ int r,k;scanf("%d%d",&r,&k); r^=last,k^=last;last=query(1,k,n+1,r); printf("%d ",last); } } } signed main(){ int t=1; scanf("%lld",&t); for(int _=1;_<=t;++_){ solve(); } return 0; }