http://acm.hdu.edu.cn/showproblem.php?pid=6703
题意:
思路1:主席树
对于+1e7,相当于将pos位置的元素删去。
对于查询,答案为min(已经删去的≥k的第一个元素,原始数列[r+1,n]的第一个大于k的元素)。
用主席树维护。(查询用了两种思路(注释和没注释的))
(我的思路(复杂了),将主席树原来加上的减去,即新开一个区域对应删除操作,由于添加和删除的链是相同的,做法是正确的)
/* (o O o)
* Author : Rshs
* Data : 2019-08-26-19.54
*/
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const LL mod = 1e9+7;
const int MX = 1e6+5;
const int max_n =2e5+5,logn=21;
int root[max_n*logn],ls[max_n*logn],rs[max_n*logn]; //下标为i的树他的左右子树的对应sum的下标
int sum[max_n*logn];//下标为i的区间(上面的区间对应进来)的数字数量
int cnt;//树的数量
int a[MX],vis[MX];
vector<int>v;set<int>s;
void update(int l,int r,int &now,int last,int pos,int ud){
now=++cnt;//对应新开的区域
ls[now]=ls[last];
rs[now]=rs[last];
sum[now]=sum[last]+ud;
if(l==r) return;
int mid=(l+r)>>1;
if(pos<=mid) update(l,mid,ls[now],ls[last],pos,ud);
else update(mid+1,r,rs[now],rs[last],pos,ud);
}
/*int Query(int L,int R,int l,int r,int t1,int t2){ //[L,R]里面元素个数
if(l>=L&&r<=R)
return sum[t2]-sum[t1];
int mid=(l+r)>>1;
int re=0;
if(mid>=L) re+=Query(L,R,l,mid,ls[t1],ls[t2]);
if(mid<R) re+=Query(L,R,mid+1,r,rs[t1],rs[t2]);
return re;
}
int query(int l,int r,int t1,int t2,int num){//区间第k大 最小为第一大
if(l==r) return (num<=sum[t2]-sum[t1])?l:1e7;
int mid=(l+r)>>1;
int cc=sum[ls[t2]]-sum[ls[t1]]; //第一棵树左儿子减第二颗树左儿子
if(num<=cc) return query(l,mid,ls[t1],ls[t2],num);
return query(mid+1,r,rs[t1],rs[t2],num-cc); //减去已有的数量
}*/
//找>=k的第一个,一直往左找可能会不满足,递归回右边的时候,必定线性找到区间,log查询到底
int query(int t1,int t2,int l,int r,int k){
if(l==r) return l;
int x1=sum[ls[t2]]-sum[ls[t1]],x2=sum[rs[t2]]-sum[rs[t1]];
int mid=(l+r)>>1;
int re=INT_MAX;
if(mid>=k&&x1) re=query(ls[t1],ls[t2],l,mid,k);//[l,mid]中可能存在一个数>=k
if(re!=INT_MAX) return re;
if(x2) re=query(rs[t1],rs[t2],mid+1,r,k);//[mid+1,r]中必定存在一个数>=k
return re;
}
int main(){
int ca;cin>>ca;while(ca--){
int n,m;cin>>n>>m;cnt=0;s.clear();v.clear();
for(int i=1;i<=n;i++)scanf("%d",&a[i]),v.push_back(a[i]),vis[i]=0;
sort(v.begin(),v.end());v.erase(unique(v.begin(),v.end()),v.end());
for(int i=1;i<=n;i++){
int id=lower_bound(v.begin(),v.end(),a[i])-v.begin()+1;
update(1,n,root[i],root[i-1],id,1);
}
int last=0;
while(m--){
int op;scanf("%d",&op);
if(op==1){
int pos;scanf("%d",&pos);pos^=last;
if(vis[pos]==0){
int id=lower_bound(v.begin(),v.end(),a[pos])-v.begin()+1;
update(1,n,root[pos],root[pos],id,-1); //将pos处的元素删去,新开一个区域
s.insert(a[pos]);
vis[pos]=1;
}
}
else {
int r,k;scanf("%d%d",&r,&k);
r^=last,k^=last;
int ans=n+1;
auto id=s.lower_bound(k);
if(id!=s.end()) ans=min(ans,*id);
/*int down=0;
if(k>1) down=Query(1,k-1,1,n,root[r],root[n]); //小于k的个数
int gg=query(1,n,root[r],root[n],down+1);*/
int gg=query(root[r],root[n],1,n,k);
ans=min(ans,gg);
cout<<ans<<'\n';
last=ans;
}
}
}
return 0;
}
思路2:线段树维护下标最大值。(权值线段树)
节点维护每一个val对应的下标的最大值,删除操作相当于将对应的下标变成n+1。
每次查询[k,n]第一个大于r的位置就是答案。
/* (o O o)
* Author : Rshs
* Data : 2019-08-26-21.40
*/
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const LL mod = 1e9+7;
const int MX = 1e6+5;
int a[MX],mx[MX],b[MX];int m,n;
void pushUp(int rt){
mx[rt]=max(mx[rt<<1],mx[rt<<1|1]);
}
void build(int l,int r,int rt){
if(l==r) {mx[rt]=b[l];return;}
int mid=(l+r)/2;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
pushUp(rt);
}
void update(int l,int r,int rt,int p){
if(l==r){mx[rt]=n+1;return ;}
int mid=(l+r)/2;
if(p<=mid) update(l,mid,rt<<1,p);
else update(mid+1,r,rt<<1|1,p);
pushUp(rt);
}
int query(int l,int r,int rt,int R,int k){ //找[k,n],第一个大于R的位置
if(l==r)return l;
int mid=(l+r)/2;
int ans=INT_MAX;
if(mx[rt<<1]>R&&k<=mid) ans=query(l,mid,rt<<1,R,k);
if(ans==INT_MAX&&mx[rt<<1|1]>R) ans=query(mid+1,r,rt<<1|1,R,k);
return ans;
}
int main(){
int T;cin>>T;while(T--){
cin>>n>>m;
for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[a[i]]=i;
build(1,n,1);
int last=0;
while(m--){
int op;scanf("%d",&op);
if(op==1){
int pos;scanf("%d",&pos);pos^=last;
update(1,n,1,a[pos]);
}
else {
int r,k;scanf("%d%d",&r,&k);
r^=last,k^=last;
int ans=n+1;
ans=min(ans,query(1,n,1,r,k));
cout<<ans<<'\n';
last=ans;
}
}
}
return 0;
}