P4303 [AHOI2006]基因匹配
P4303 [AHOI2006]基因匹配
题目链接 :https://www.luogu.com.cn/problem/P4303
大意 给出一个n,和两个长度为 5n 的序列,这两个序列中,1-n每个数都出现5次,求最长公共子序列。
Sample input
2
1 1 1 1 1 2 2 2 2 2
1 1 1 2 2 2 2 2 1 1
Sample output
8
分析
n最大20000,按通常求lcs的方法会t掉
注意一下这两个序列的特殊性,有很多重复的元素。
我们可以记录一下每种元素的位置,拿样例举个栗子吧:
对于序列1: 1出现的位置:1 2 3 4 5 2 出现的位置 6 7 8 9 10
把序列2中的元素换成他们的位置:
(1 2 3 4 5)(1 2 3 4 5)(1 2 3 4 5)(6 7 8 9 10)(6 7 8 9 10)(6 7 8 9 10)(6 7 8 9 10)(6 7 8 9 10)(1 2 3 4 5)(1 2 3 4 5)
把位置换成倒序(为什么呢):
(5 4 3 2 1)(5 4 3 2 1)(5 4 3 2 1)(10 9 8 7 6)(10 9 8 7 6)(10 9 8 7 6)(10 9 8 7 6)(10 9 8 7 6)(5 4 3 2 1)(5 4 3 2 1)
对这个东西求一下lis就可以了,我们有nlogn的算法。
什么原理呢?
序列2中的位置必须满足升序,才能和序列1中的元素对应起来。
而我们把位置变成倒序,是为了保证每个点只取了1次
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 1000000;
int n, x, tot, a[maxn], low[maxn];
vector<int> v[maxn];//v[i]存储i可能的位置
signed main(){
scanf("%d", &n);
for(int i=1; i<=5*n; i++){
scanf("%d", &x);
v[x].push_back(i);记录位置
}
for(int i=1; i<=5*n; i++){
scanf("%d", &x);
for(int j=4; j>=0; j--){
a[++tot] = v[x][j];//倒序把顺序存入a
}
}
low[1] = a[1];
int ans = 1;
for(int i=2; i<=tot; i++){//对a求lis,不多说
if(a[i] > low[ans]) low[++ans] = a[i];
else{
int x = lower_bound(low+1, low+1+ans, a[i]) - low;
low[x] = a[i];
}
}
printf("%d\n", ans);
return 0;
}