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;
}
posted @ 2020-04-22 21:43  poozhai  阅读(115)  评论(0编辑  收藏  举报