BZOJ3339 Rmq Problem【离线/可持久化数据结构】

Darkbzoj


在这里插入图片描述


SOL

法一:维护每一个值在原序列的最右出现位置的最小值,对于二分的左半部分,如果最小值大于等于当前左端点,说明已满。
用主席树排除右端点超出qr的影响。在主席树上二分

法二:增量法思想。 从[l,r] 到[l+1,r],只有a[l]可能能够拿来更新一段区间的mex,写一个以左端点为第一维的支持区间修改min,单点查询(可持久化标记)的主席树。
(也可以离线,用线段树做)

法三:可持久化trie(或者可持久化非旋treap),这样可以提出区间,再二分找点即可(类似法一)


CODE

#include<bits/stdc++.h>
using namespace std;
#define sf scanf
#define pf printf
#define ll long long
#define cs const
#define db double
#define ri register int
#define gc getchar()
#define in red()
inline int red(){
	int num=0,f=1;char c=gc;
	for(;!isdigit(c);c=gc)if(c=='-')f=-1;
	for(;isdigit(c);c=gc)num=(num<<1)+(num<<3)+(c^48);
	return num*f;
}
cs int N=1e7+10,inf=1e9,M=2e5+10;
int mex[M],nxt[M],las[M],vis[M],num[M],n,m;
int tag[N],tot,ch[N][2],rt[M];
#define lc(x) ch[x][0]
#define rc(x) ch[x][1]
inline int cpy(int x){++tot;tag[tot]=tag[x];ch[tot][0]=ch[x][0];ch[tot][1]=ch[x][1];return tot;}
inline void upt(int p,int l,int r,int ql,int qr,int &t,int v){
//	cout<<v<<' '<<ql<<' '<<qr<<'\n';
//	cout<<p<<'\n';
	t=cpy(p);
	if(ql>qr)return;
	if(ql<=l&&r<=qr)return tag[t]=min(tag[t],v),void();
	int mid=(l+r)>>1;
	if(ql<=mid)upt(lc(p),l,mid,ql,qr,lc(t),v);
	if(qr>mid)upt(rc(p),mid+1,r,ql,qr,rc(t),v);
}
int tmp;
inline void query(int p,int l,int r,int k){
//	cout<<l<<' '<<r<<' '<<tag[p]<<'\n';
	tmp=min(tag[p],tmp);
	if(l==r)return;
	int mid=(l+r)>>1;
	if(k<=mid)query(lc(p),l,mid,k);
	else query(rc(p),mid+1,r,k);
}
inline void build(int l,int r,int &t){
	t=++tot;tag[t]=inf;
	if(l==r)return tag[t]=mex[l],void();
	int mid=(l+r)>>1;
	build(l,mid,lc(t));build(mid+1,r,rc(t));
}


signed main(){
//	freopen("data.in","r",stdin);
	n=in;m=in;
	for(ri i=1;i<=n;++i)num[i]=in;
	int k=0;
	for(ri i=1;i<=n;++i){
		vis[num[i]]=1;
		while(vis[k])++k;
		mex[i]=k;
	}
	for(ri i=0;i<=200000;++i)las[i]=n+1;
	for(ri i=n;i>=1;--i)nxt[i]=las[num[i]],las[num[i]]=i;
//	for(ri i=1;i<=n;++i)cout<<nxt[i]<<'\n';
	build(1,n,rt[1]);
	for(ri i=2;i<=n;++i){
		upt(rt[i-1],1,n,i,nxt[i-1]-1,rt[i],num[i-1]);
	}
	while(m--){
		int l=in,r=in;
		tmp=inf;
		query(rt[l],1,n,r);
		cout<<tmp<<'\n';
	}



	return 0;
}














  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值