P8986 [北大集训 2021] 基因编辑

Question 问题 P8986 [北大集训 2021] 基因编辑

给定一个长度为 \(n\) 的序列 \(a\) 以及需要切割的范围 \(l,r\),求其中最短的合法子序列 \((x,y)\) 满足:

  1. \(x<l~~y>r\)
  2. 不存在 \((x',y')\) 满足 \(a_{x'}=a_x~~a_{y'}=a_y\)

Analysis 分析

令:

\(pre_i\)\(a_i\) 这个颜色在 \(i\) 前第一次出现的下标。

\(lst_i\)\(a_i\) 这个颜色最后一次出现的下标。

由反证法易得:我们选出来的 \(x,y\) 只有可能是某种颜色的第一次出现的位置和最后出现的位置。同时还必须满足 \(pre_y<x\)\(y\) 在区间 \((r,n]\) 只出现一次,\(x\) 在区间 \([1,l)\) 只出现过一次。

Solution

从小到大枚举 \(y\),开一个 set 维护维护在 \([1,l)\) 出现一次的 \(x\)。到区间 \((r,n]\) 选一个只出现过一次的 \(y\) 后,再从 set 中找到一个最大的下标并更新答案。记得判断无解。

Code 代码

int n,l,r,ans=inf;
int a[N],pre[N],lst[N],now[N];//如题解意思,now 是为了方便清除 set
int v[N];// v[i]=a[i] 在区间 (r,n] 第一次出现,用来判断是不是唯一出现。
set<int> s;
int main(){
	read(n,l,r);
	for(rint i=1;i<=n;i++){
		read(a[i]);
		pre[i]=lst[a[i]];lst[a[i]]=i;
		if(i>r && !v[a[i]]) v[a[i]]=i;
	}
	for(rint i=1;i<=n;i++){
		if(i<l && !now[a[i]]) s.insert(i);
		else if(s.find(now[a[i]])!=s.end()) s.erase(now[a[i]]);
		if(!now[a[i]]) now[a[i]]=i;
		if(i>r && v[a[i]]==i && i==lst[a[i]] && s.size()){
			int t=*(--s.end());
			if(t>=pre[i]) ans=min(ans,i-t+1);
		}
	}
	if(ans==inf) ans=-1;
	printf("%d\n",ans);
    return 0;
}

后记

该方法常数较大,请开 O2。如有不开 O2 的方法,请私信我谢谢。

posted @ 2024-05-29 21:55  Mr_Azz  阅读(6)  评论(0编辑  收藏  举报