acm-(线段树、mex)Codeforces Round #678 (Div. 2) E. Complicated Computations

题面
传送门
本题不好直接求每个子串的 m e x mex mex,转化一下思路,对于 i i i而言,我们不妨检查是否存在一个子串使得 m e x = i mex=i mex=i

现在讨论满足怎样条件的一个子串 s [ l ∼ r ] s[l\sim r] s[lr] m e x = i mex=i mex=i

  1. s [ l ∼ r ] ≠ i s[l\sim r]\ne i s[lr]=i
  2. 1 , 2 , . . . , i − 1 ∈ s [ l ∼ r ] 1,2,...,i-1\in s[l\sim r] 1,2,...,i1s[lr]

于是我们遍历 s s s数组,以当前位置 r + 1 r+1 r+1减去一后的 r r r为右端点,查找一个最远的左端点 l l l使得 s [ l ∼ r ] ≠ s [ r + 1 ] s[l\sim r]\ne s[r+1] s[lr]=s[r+1],然后再询问 1 ∼ s [ r + 1 ] − 1 1\sim s[r+1]-1 1s[r+1]1中所有数上一次出现的最远位置 x x x,如果 x < l x<l x<l,那么说明该串 m e x mex mex必不为 s [ r + 1 ] s[r+1] s[r+1],否则必为。

具体实现的话,用 l a s t [ s [ i ] ] last[s[i]] last[s[i]]记录 s [ i ] s[i] s[i]上一次出现的最近位置,用线段树维护 1 ∼ n 1\sim n 1n每个数的 l a s t last last值,并能做到查询区间最小值即可。

关于1的话可能要特判,另外答案最大值可能为 n + 2 n+2 n+2,不是 n + 1 n+1 n+1!!


int n,a[maxn],t[maxn<<2],last[maxn],vis[maxn];
void pushup(int rt){
	t[rt]=min(t[rt<<1],t[rt<<1|1]);
}
int qry(int rt,int l,int r,int ql,int qr){
	if(r<ql || l>qr)return 1e9;
	if(ql<=l && r<=qr)return t[rt];
	int mid=l+r>>1;
	return min(qry(rt<<1,l,mid,ql,qr),qry(rt<<1|1,mid+1,r,ql,qr));
} 
void cg(int rt,int l,int r,int x,int val){
	if(l==r){
		t[rt]=val;
		return;
	}
	int mid=l+r>>1;
	if(x<=mid)cg(rt<<1,l,mid,x,val);
	else cg(rt<<1|1,mid+1,r,x,val);
	pushup(rt);
}
int main(){
	int n=rd();
	FOR(i,1,n+1){
		a[i]=rd();
		if(a[i]!=1){
			int x=qry(1,1,n,1,a[i]-1);
			vis[1]=1;
			if(x>last[a[i]])vis[a[i]]=1;
		}else vis[2]=1;
		cg(1,1,n,a[i],i);
		last[a[i]]=i;
	}
	last[n+1]=0;
	FOR(i,2,n+2){
		int x=qry(1,1,n,1,i-1);
		if(x>last[i])vis[i]=1;
	}
	FOR(i,1,n+3)if(!vis[i])return wrn(i),0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值