2018.10.31 bzoj3339&&3585mex(主席树)

传送门
双倍经验
直接上主席树,每个叶节点维护这个值出现的最右区间,非叶子节点维护当前值域内所有最右区间的最小值。
查询的时候只用在以 r o o t [ q r ] root[qr] root[qr]为根的树上面二分。
看左儿子满没有就只用看左边的最小值是不是大于 q l ql ql就行了。
代码:

#include
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
const int N=2e5+5;
int n,q,rt[N],mex[N*50],son[N*50][2],tot=0,sig=0;
inline void update(int&p,int las,int l,int r,int k,int v){
	p=++tot,son[p][0]=son[las][0],son[p][1]=son[las][1],mex[p]=mex[las];
	if(l==r){mex[p]=v;return;}
	int mid=l+r>>1;
	if(k<=mid)update(son[p][0],son[las][0],l,mid,k,v);
	else update(son[p][1],son[las][1],mid+1,r,k,v);
	mex[p]=min(mex[son[p][0]],mex[son[p][1]]);
}
inline int query(int p,int l,int r,int k){
	if(l==r)return l;
	int mid=l+r>>1;
	if(mex[son[p][0]]>=k)return query(son[p][1],mid+1,r,k);
	return query(son[p][0],l,mid,k);
}
int main(){
	n=read(),q=read();
	for(int i=1;i<=n;++i)update(rt[i],rt[i-1],0,1e9,read(),i);
	for(int i=1,l,r;i<=q;++i)l=read(),r=read(),printf("%d\n",query(rt[r],0,1e9,l));
	return 0;
}

你可能感兴趣的:(#,主席树)