题目链接:HDU - 6703 array
我们可以先来看一下,官方的题解:
题解
因为数组中的值唯一,且在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的最小值,?不就是主席树吗?
接下来,我们处理第一个问题即可,直接把元素放进set,没在set中二分,找到最小的大于等于K的元素,与我们在主席树当中得到的元素比较,取最小值。
需要注意的点:
- 每次放进set的时候,只有当这个元素没有被放过我们才放。
- 最大答案为n+1。
- 主席树要初始化cnt=0。
- ask当中需要套query。
- 在线查询。
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=1e5+10,inf=0x3f3f3f3f;
int T,n,m,res,cnt,rt[N],vis[N],a[N],L,R;
struct node{int l,r,sum;}t[N*20];
set<int> st;
void change(int l,int r,int &x,int y,int k){
x=++cnt; t[x]=t[y]; t[x].sum++;
if(l==r) return ; int mid=l+r>>1;
if(k<=mid) change(l,mid,t[x].l,t[y].l,k);
else change(mid+1,r,t[x].r,t[y].r,k);
}
int query(int l,int r,int ql,int qr,int x,int y){
if(l==ql&&r==qr) return t[y].sum-t[x].sum;
int mid=l+r>>1;
if(qr<=mid) return query(l,mid,ql,qr,t[x].l,t[y].l);
else if(ql>mid) return query(mid+1,r,ql,qr,t[x].r,t[y].r);
else return query(l,mid,ql,mid,t[x].l,t[y].l)+query(mid+1,r,mid+1,qr,t[x].r,t[y].r);
}
int ask(int l,int r,int x,int y,int k){
if(l==r) return (t[y].sum-t[x].sum?l:n+1); int mid=l+r>>1;
if(k<=mid&&query(1,n,k,mid,L,R)) return ask(l,mid,t[x].l,t[y].l,k);
else return ask(mid+1,r,t[x].r,t[y].r,k);
}
signed main(){
cin>>T;
while(T--){
cin>>n>>m; res=cnt=0; st.clear(); memset(vis,0,sizeof vis);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),change(1,n,rt[i],rt[i-1],a[i]);
while(m--){
int op,x,y; scanf("%d %d",&op,&x); x^=res;
if(op==1){
if(!vis[x]) vis[x]=1,st.insert(a[x]);
}
else{
scanf("%d",&y); y^=res; x++; int ans=n+1;
if(x<=n) L=rt[x-1],R=rt[n],ans=ask(1,n,L,R,y);
auto u=st.lower_bound(y); if(u!=st.end()) ans=min(ans,*u);
res=ans; printf("%d\n",res);
}
}
}
return 0;
}