zjhu1021mex函数(可持久线段树)

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int tot,n,m,ver[maxn];
struct tt{
	int l,r,val;
	//val:某个时间(ver)下 该树内所有数都出现的 最后位置 
}tree[maxn<<5];
int build(int l,int r){
	int rt=++tot;
	tree[rt].val=0;
	tree[rt].l=rt;
	tree[rt].r=rt;
	return rt;
}
void pushup(int root){
	tree[root].val=min(tree[tree[root].l].val,tree[tree[root].r].val);
	//要以root为根的树下的所有数都出现,都所以取min 
}
int change(int node,int l,int r,int x,int p){
	//x在p位置上出现 
	//xlr是数,p是状态时间位置
	int root=++tot;
	tt &rt=tree[root];
	rt=tree[node];
	//找到x的"位置"了 
	if(l==r){		
		rt.val=p;
		//因为每次change是按时间序来的,所以就p是x在该时刻最晚出现的位置 
		return root;
	}
	int mid=(l+r)>>1;
	if(x<=mid) rt.l=change(rt.l,l,mid,x,p);
	else rt.r=change(rt.r,mid+1,r,x,p);
	pushup(root);
	return root;
}
int query(int node,int L,int R,int l,int r){
	if(l==r) return l;
	tt &rt=tree[node];	
	tt &rl=tree[rt.l],&rr=tree[rt.r];
	int mid=(l+r)>>1;
	//左边的数都出现的最晚位置>=L,说明左边数都出现了。右边同理
	//<L,就说明还有数没出现过。因为要求最小的没出现过的,所以先判断左再判断右 
	if(rl.val<L) return query(rt.l,L,R,l,mid);
	else if(rr.val<L)  return query(rt.r,L,R,mid+1,r);
	else return n;//左右都>=L说明全出现过了。就返回n 
}
int main(){
	int t,x,L,R;
	cin>>t;
	while(t--){
		tot=0;
		scanf("%d%d",&n,&m);
		ver[0]=build(0,n);
		for(int i=1;i<=n;i++){
			scanf("%d",&x);
			x=x>n?n:x;//>n的数用n代替,因为题目只考虑0~n-1这些数是否出现,其它数可一律看做n 
			ver[i]=change(ver[i-1],0,n,x,i);
		}
		for(int i=1;i<=m;i++){
			scanf("%d%d",&L,&R);
			printf("%d\n",query(ver[R],L,R,0,n));
			//用R时刻的树来看,将其最晚位置和L比较 
		}
	}
	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_50904510/article/details/120017356