题解:[北大集训 2021] 基因编辑

洛谷P8986

Posted by TH911 on August 11, 2025

题目传送门

本题解主要用于翻译题面。

难度在于读题

题面说了一大堆 HD1048576d 外星人、外星生命的基因序列、基因编辑技术等内容,以至于读题非常不容易。考虑如何去有条理地、清晰地正确理解题目。

输入给出了正整数序列 $s_1,s_2,\cdots,s_n$,和一个数对 $(l,r)$。

先找与之相关的信息。

对于一段长度为 $n$ 的外星生命的基因序列(不妨记其正整数表示为 $s_1, s_2, \cdots, s_n$),外星文明 HD1048576d 的基因编辑过程如下:

  1. 选择一段要编辑的区域 $[l, r]$,即原位替换原序列中 $s_l, s_{l+1}, \cdots, s_r$ 这部分子序列;
  2. 挑选一对跨过待替换区域的下标 $(i, j)$(即 $1\le i<l$ 且 $r<j\le n$),批量生产出 $s_i, \cdots, s_j$ 这段子序列在编辑后对应的新序列 $s_i, \cdots, s_{l-1}, t_1, \cdots, t_k, s_{r+1}, \cdots, s_j$;
  3. 通过对应的特异性识别工具,将 $s_i, \cdots, s_j$ 这段子序列从原序列中断开,并将 $s_i, \cdots, s_{l-1}, t_1, \cdots, t_k, s_{r+1}, \cdots, s_j$ 接到序列中,即可得到目标基因序列。

需要注意的是,在步骤 2 中,挑选的这对下标必须对应唯一的 noicleobase 组合。也就是说,能够满足 $s_{i’}=s_i, s_{j’} = s_j$ 且 $i<j$ 的有序对 $\left(i’, j’\right)$ 必须是唯一的(即为 $(i, j)$),否则特异性识别工具可能切割下其它区段的基因序列;另外,$s_i\ne s_j$,否则特异性识别工具可能只切割下单个 noicleobase。

即找 $1\leq i<l,r<j\leq n$,满足 $s_i\neq s_j$,且不存在 $1\leq i’<i,s_{i’}=s_i$ 或 $j<j’\leq n,s_j=s_{j’}$。

再找输出相关的信息。

另外,由于替换时需要生产新的基因序列,而生产这样的序列需要不小的开销,所以外星文明希望能够最小化需要生产的基因序列长度。显然,最小化这一长度等价于最小化被切割下来的基因子序列的长度,所以实践中一般是通过最小化被切割下来的基因子序列长度来计算最优解的。

即找最小的子串长度,即最小的 $j-i+1$。


因此,可以明确题意:给定正整数序列 $s_1,s_2,\cdots,s_n$ 和数对 $(l,r)$,问是否存在 $L,R$,满足 $1\leq L<l,r<R\leq n$,满足 $s_L\neq s_R$,且不存在 $1\leq i<L,s_i=s_L$ 或 $R<j\leq n,s_R=s_j$。

  • 若存在,输出最小的 $R-L+1$。
  • 否则,输出 $-1$。

同时可以注意到,前面一段都是废话,实际上也如此。

模拟赛读题读了一小时,还挂分了。

维护过程

记 $\textit{pre}_i,\textit{suf}_i$ 分别表示 $s_i$ 在 $i$ 之前的出现位置和 $i$ 之后的出现位置。特别地,若不存在,则钦定 $\textit{pre}_i=0,\textit{suf}_i=+\infty$。

考虑枚举 $L$,并确定最小的合法的 $R$。合法的 $L$ 需要满足的条件有:

\[1\leq L<l\\ \textit{pre}_L=0\]

与之对应的合法的 $R$ 需要满足的条件有:

\[r<R\leq n\\ \textit{suf}_R=+\infty\\\]

注意到 $s_L\neq s_R$,因此还有:

\[\textit{pre}_R<L\]

考虑用数据结构去维护 $R$。前两个条件很好满足,第三个条件与动态枚举的 $L$ 有关,需要处理。

  • 当 $\textit{pre}_i=0$ 时,$i$ 一定是一个可用的 $R$。
  • 否则可以在 $L$ 枚举到 $\textit{pre}_i+1$ 时,确定 $i$ 在此之后是一个可用的 $R$。

记可用 $R$ 的集合为 $S$,对于一个 $L$,即找到一个 $x\in S$ 满足 $r<x$ 且 $x$ 最小。平衡树维护即可。

时间复杂度 $\mathcal O(n\log n)$。

AC 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
#include<set>
using namespace std;
constexpr const int N=1e6,inf=0x3f3f3f3f;
int n,a[N+1],pos[N+1],pre[N+1],suf[N+1];
int insert[N+1];
set<int>t;
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	int l,r;
	cin>>n>>l>>r;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		pre[i]=pos[a[i]];
		pos[a[i]]=i;
	}
	memset(pos,0x3f,sizeof(pos));
	for(int i=n;i>=0;i--){
		suf[i]=pos[a[i]];
		pos[a[i]]=i;
	}
	set<int>R;
	for(int i=r+1;i<=n;i++){
		if(pre[i]<l&&suf[i]==inf){
			if(!pre[i]){
				R.insert(i);
			}else{
				insert[pre[i]]=i;
			}
		}
	}
	int ans=2147483647;
	for(int L=1;L<l;L++){
		if(!pre[L]){
			auto p=R.upper_bound(r);
			if(p!=R.end()){
				if(*p<=suf[L]){
					ans=min(ans,*p-L+1);
				}
			}
		}
		if(insert[L]){
			R.insert(insert[L]);
		}
	}
	if(ans==2147483647){
		cout<<"-1\n";
	}else{
		cout<<ans<<'\n';
	}
	
	cout.flush();
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}