1. 程式人生 > 實用技巧 >CF1436E Complicated Computations 題解

CF1436E Complicated Computations 題解

Problem

給定一個長度為 \(n(n \leq 10^5)\) 的陣列,求出它所有子陣列 \(MEX\)\(MEX\)

這裡的\(MEX\) 定義為一個數組中第一個沒有出現的 正整數

Solution

考場沒有想出來,自閉了。

我們考慮是否存在一個子陣列,滿足其\(MEX=a\) ,首先,這個子數組裡面必須要沒有 \(a\)

於是我們首先把陣列中所有的 \(a\) 找出來,這些 \(a\) 把陣列分成了若干段,我們要的子陣列必須不能跨越這些段。

其次,對於這些段中,如果所有小於\(a-1\)的數都在裡面,那麼這一段一定滿足\(MEX=a\) (因為裡面沒有\(a\))。

可以結合一下圖理解。

我們可以用線段樹維護,具體細節見程式碼。

Code

const int MAXN = 1e5+10;

int val[MAXN<<2],lst[MAXN],a[MAXN],n;
bool able[MAXN];

//這個線段樹維護的是每個數最後出現的位置

void update(int o,int l,int r,int pos,int v){
	if(l == r) return (void)(val[o] = v);
	int mid = l+r>>1;
	if(pos <= mid) update(o<<1,l,mid,pos,v);
	else update(o<<1|1,mid+1,r,pos,v);
	val[o] = min(val[o<<1],val[o<<1|1]);
}//更新區間最小值

int query(int o,int l,int r,int xl,int xr){
	if(l == xl && r == xr) return val[o];
	int mid = l+r>>1;
	if(xr <= mid) return query(o<<1,l,mid,xl,xr);
	else if(xl > mid) return query(o<<1|1,mid+1,r,xl,xr);
	else return min(query(o<<1,l,mid,xl,mid),query(o<<1|1,mid+1,r,mid+1,xr));
}//查詢區間最小值

int main (){//lst[i] i 上一次出現的位置
	scanf("%d",&n);
	for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
	for(int i = 1;i <= n;i++){
		if(a[i] != 1) able[1] = 1;//注意對1的特判
		if(a[i] > 1  && query(1,1,n,1,a[i]-1) > lst[a[i]]) able[a[i]] = 1;
      //對段進行分割處理(lst[a[i]] ~ i) ,如果 1~a[i]-1 的數最後出現的位置都 > lst[a[i]] 且 < i(因為後面的還沒更新,所以必定 < i) ,那麼這一段滿足MEX = a[i]
		lst[a[i]] = i;
		update(1,1,n,a[i],i);
	}
	for(int i = 2;i <= n+1;i++) if(query(1,1,n,1,i-1) > lst[i]) able[i] = 1;
  //因為lst[i] 初始為0,所以之前我們處理了 1~i 最先出現的位置的段,但是沒有處理 lst[i] ~ 結尾 的段,這裡在處理一遍
	int ans = 1;
	for(;able[ans] && ans <= n+1;ans++);
  //查詢答案,注意ans 上界實際上為 n+2
	printf("%d\n",ans);
	return 0;
}