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;
}