(CCPC) 网络赛 1002 array (线段树剪枝)

传送门

题意:给你一个序列,每次查询【1,r】>=k 最小的没有出现的ai值。

解:就是一颗权值线段树,同时节点用ls维护他代表区间所有值出现位置的最大值,没有出现(或修改)都置成无穷大,查询的时候,走左右儿子根据k来,满足条件优先进入左儿子,但是要判断一下ls(lson)是否>r,如果小于r,那说明所有值都在小于r的区间出现了,就没必要进入。然后递归到叶子节点,出现的位置>r,那就说明答案是这个l了。在左儿子没有找到答案的话,在回溯的过程中,如果可以进入右儿子,那就说明答案一定会在右儿子区间中了,因为右儿子区间值>k且存在值的下标>r,所以只要找到下表>r最小的数即可。最坏的情况在树中走出的情况就是一个人形的样子,2*log。

#include<bits/stdc++.h>
#define il inline
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1e7+5;
int a[maxn],b[maxn],ls[maxn];
int fg[maxn];
il void pushup(int rt){
	ls[rt]=max(ls[rt<<1],ls[rt<<1|1]);
}
il void build(int l,int r,int rt){
	if(l==r){
		ls[rt]=b[l];
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,rt<<1);
	build(mid+1,r,rt<<1|1);
	pushup(rt);
}
il void update(int l,int r,int rt,int x){
	if(l==r){
		ls[rt]=b[l]=inf;
		return ;
	}
	int mid=(l+r)>>1;
	if(x<=mid) update(l,mid,rt<<1,x);
	else update(mid+1,r,rt<<1|1,x);
	pushup(rt);
}
int ans;
il void query(int l,int r,int rt,int R,int k){
	if(l==r){
		if(ls[rt]>R) ans=l;
		return ;
	}
	int mid=(l+r)>>1;
	if(k<=mid && ls[rt<<1]>R) query(l,mid,rt<<1,R,k);
	if((k>mid || ans==inf) && ls[rt<<1|1]>R) query(mid+1,r,rt<<1|1,R,k);
}
int T,n,m;
int main(){
	scanf("%d",&T);
	for(int t=1;t<=T;++t){
		int lans=0;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;++i){
			scanf("%d",&a[i]);
			b[a[i]]=i;
		}
		for(int i=n+1;i<=n+10;++i) b[i]=inf;
		build(1,n+10,1);
		int op,t1,t2,r,k;
		for(int i=1;i<=m;++i){
			scanf("%d%d",&op,&t1);
			if(op==1){
				t1=t1^lans;
				if(fg[t1]!=t) update(1,n+10,1,a[t1]),fg[t1]=t;
			}
			else{
				scanf("%d",&t2);
				r=t1^lans,k=t2^lans;
				ans=inf;
				query(1,n+10,1,r,k);
				printf("%d\n",ans);
				lans=ans;
			}
		}
	}
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值