题目大意:
T个样例。给你一个长度为n的数组a,1≤a[i]≤n,a[i]各不相同。m个操作。ans初始为0。有两种操作:
操作1:给你t1。pos=t1^ans。把数组下标为pos的数,数值+1e7;
操作2:给你t2,t3。r=t2^ans,k=t3^ans。输出**与数组下标1~r的数不同**且**不小于k**的最小数。更新ans。
数据范围:
1≤T≤10,1≤n≤1e5,1≤m≤1e5,1≤a[i]≤n,a[i]各不相同,1≤pos≤n,1≤r≤n,1≤k≤n。
∑n≤510,000,∑m≤510,000。
赛后补题。
此处照搬标准题解:
因为数组中的值唯一,且在1到n的范围内,而询问的r和k也在1到n的范围内。 所以对于任意一个被操 作1修改过的值都不会成为询问的答案,而询问的结果也必然在k到n+1的范围内。 因为没有被修改过 值是唯一的,所以可以建立权值线段树,维护权值区间内的值所在下标的最大值。而询问则转化为不小 于k的值里面,下标超过r的最小权值是多少。 如何处理询问呢,一种较为暴力的解法是直接在线段树上 询问权值在k到n+1的范围内第一个下标超过r的权值是多少。但复杂度可能会被卡,需要减枝。 再加上 一个额外的判断就可以了,就是在递归查询完左子树内存不存在大于r的下标之后,如果不存在,则先 看一下右子树内的下标的最大值是否大于r。如果不大于r,则不必再进入右子树内查询,否则答案一定 在右子树内。在进左子树之前也利用同样的判断条件来判断是否有必要进入左子树,这样做可以保证单 次查询的复杂度是O(log n) 的。 而对于操作1,则等价于修改某个权值的下标为无穷大。操作复杂度也 是O(log n )的。 综上所述,得到了一个复杂度为O( m * log n )的在线算法,可以较快地通过此题。
理解:
由于询问结果在k到n+1的范围内,
所以我们每次只要以权值(数值)[k,n] 为区间 查找满足下标大于r的尽可能小的数。递归查找。但这样暴力做会TLE,所以需要剪枝。
尽可能小的数就是尽可能取左子树的值,能取左子树的值(即区间左部分的值),就不取右子树的值,所以,
可以维护权值区间[l,r]里所有的数的最大下标,
一旦左子树的最大下标满足大于操作2的r,就可以搜左子树的值,而不去再找右子树的值。
一旦左子树的最大下标小于或等于操作2的r,就不搜左子树,直接进行右子树的判断和搜索。倘若右子树符合要求,则进行搜索,不符合则跳出,答案为n+1。
对于操作1的修改,因为每个数只出现一次,所以修改过的原数值一定不会出现在[1,r]区间内(当然在任意区间都不可能出现),所以操作1修改过的数一定符合搜所的要求,所以每次进行操作1,则更新一次下标最大值,更改为无穷大,保证一定满足所有搜索要求。
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+50; const int inf=0x3f3f3f3f; /**********建树********/ struct P{ int l,r,id; }tree[maxn<<2]; void build(int l,int r,int k) { tree[k].l=l;tree[k].r=r; if(tree[k].l==tree[k].r) { tree[k].id=0; return ; } int mid=(tree[k].l+tree[k].r)/2; build(l,mid,k*2); build(mid+1,r,k*2+1); tree[k].id=0; } /*********修改下标最大值***********/ void add(int x,int id,int k) { if(tree[k].l==tree[k].r) { tree[k].id=id;return; } int mid=(tree[k].l+tree[k].r)/2; if(x<=mid)add(x,id,k*2); else add(x,id,k*2+1); tree[k].id=max(tree[k*2].id,tree[k*2+1].id); } /**********对操作2进行查找*************/ int find(int l,int r,int k,int minid) { if(tree[k].l==tree[k].r) { if(tree[k].id>minid)return l; return inf; } if(tree[k].id<=minid)return inf; int mid=(tree[k].l+tree[k].r)/2; int id1=tree[k*2].id,id2=tree[k*2+1].id; if(r<=mid) { if(id1<=minid)return inf; return find(l,r,k*2,minid); } if(l>mid) { if(id2<=minid)return inf; return find(l,r,k*2+1,minid); } int ans1=inf,ans2=inf,ans=inf; if(id1>minid)ans1=find(l,mid,k*2,minid); if(id2>minid&&ans1==inf)ans2=find(mid+1,r,k*2+1,minid); ans=min(ans1,ans2); return ans; } /*void debug(int l,int r,int k) { printf("##l:%d ##r:%d##id:%d ",tree[k].l,tree[k].r,tree[k].id); if(tree[k].l==tree[k].r)return; int mid=(tree[k].l+tree[k].r)/2; debug(l,mid,k*2); debug(l,mid,k*2+1); }*/ int main() { int T; scanf("%d",&T); while(T--) { int n,m,op,t1,t2,t3,i,a[maxn],ans=0; scanf("%d%d",&n,&m); build(1,n,1); for(i=1;i<=n;i++) { scanf("%d",&a[i]); add(a[i],i,1); } //debug(1,n,1); for(i=1;i<=m;i++) { scanf("%d",&op); if(op==1) { scanf("%d",&t1); add(a[t1^ans],inf,1);//这里输入的是下标,不是数值 >皿< //debug(1,n,1); continue; } scanf("%d%d",&t2,&t3); t2^=ans;t3^=ans; ans=find(t3,n,1,t2); //printf("@@r:%d k:%d@@ans:",t2,t3); if(ans!=inf)printf("%d ",ans); else{ ans=n+1; printf("%d ",ans); } } //puts("############"); } }