2020 算法上机赛 C2 - C 原神的故事 No.3

题目描述

今天蒙德举行马拉松比赛,参赛选手有可莉和七七,比赛路线中有 \(n\) 个点,两位选手的路线分别是一个 \(1\sim n\) 的排列,他们要按照各自的序列从第一个点开始,依次经过序列中的点到达最后一个点,现在小田田想要知道他们在比赛中最多能在几个点的位置相遇,你能告诉他吗

输入

第一行有一个数 \(n\)

接下来两行各 \(n\) 个数,为 \(1\sim n\)的一个排列

输出

一个数字,含义参见题目

输入样例

6
1 3 5 6 2 4
3 6 1 2 5 4

输出样例

4

数据范围

n<=10^5

提示

最~长~公~共~子~序~列~啊~~~~

做法

(题面是直接爬下来的,所以风格可能会一直变)

两个排列都是乱序的时候,不太容易直接想出来

先考虑一个简单情况:

6
1 2 3 4 5 6
3 6 1 2 5 4

如果有一个是有序的,那么如何寻找公共子序列呢?

不难发现,第二个数组中任意一个单调递增的子序列,都是公共子序列

在这个特殊的情况下,就变成了在第二个数组中寻找 最长上升子序列,这个问题是可以 \(O(n\log n)\) 解决的

那么不特殊的情况呢? 其实只要做一个 映射 就好了

比如样例中

6
1 3 5 6 2 4
3 6 1 2 5 4

做映射 (实际上是双射)

1 -> 1
3 -> 2
5 -> 3
6 -> 4
2 -> 5
4 -> 6

就变成了

6
1 2 3 4 5 6
2 4 1 5 3 6

然后只要在 2 4 1 5 3 6 中找最长上升子序列,就是答案了

代码写起来意外的简单

#include <bits/stdc++.h>

using namespace std;
inline int read() {
    int q=0,w=1;char c=getchar();
    while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
    while(isdigit(c))q=q*10+c-'0',c=getchar();
    return w*q;
}

const int N = 1e5 + 100;
const int INF = 0x3f3f3f3f;
int n, a[N], b[N], dp[N];

int main() {
    n = read();
    for(int i = 1;i <= n; ++i) 
        a[read()] = i;
    for(int i = 1;i <= n; ++i)
        b[i] = a[read()];
    for(int i = 1;i <= n + 1; ++i) dp[i] = INF;
    for(int i = 1;i <= n; ++i)
        *lower_bound(dp + 1, dp + 1 + n + 1, b[i]) = b[i];
    cout << lower_bound(dp + 1, dp + 1 + n, INF) - dp - 1 << endl;
    return 0;
}
posted @ 2020-12-31 14:07  Withinlover  阅读(82)  评论(0编辑  收藏  举报