链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1264
思路: n大小为20000*5,而一般的dp求最长公共子序列复杂度是 n*n的,所以我们必须优化。
题目说了一个数会出现5次,那么我们可以预处理得到 第一个序列a[]每个数字分别在哪些位置,
因为求LCS的状态转移方程中当 s1[i-1] == s2[j-1]时,dp[i][j] = dp[i-1][j-1] + 1;只有当两个点相同时
值才会+1,我们可以对第二个序列b[]遍历一遍,对于b[i]我们可以找到它在a[]上的5个位置,这5个
位置的dp[pos]都可以被更新,状态转移方程为: dp[pos] = max(p[1] - p[pos-1]) + 1, 对于dp[1] - dp[pos],
这段区间的最大值,我们直接用树状数组维护就好了,时间复杂度为 O(n*logn)
实现代码:
#include<bits/stdc++.h> using namespace std; #define ll long long const int M = 2e5+10; int a[M][7],c[M],dp[M],n; void update(int x,int p){ while(x <= n*5){ c[x] = max(c[x],p); x += (x&-x); } } int getsum(int x){ int ans = 0; while(x){ ans = max(ans,c[x]); x -= (x&-x); } return ans; } int main() { int x; cin>>n; for(int i = 1;i <= n*5;i ++){ cin>>x; a[x][++a[x][0]] = i; } for(int i = 1;i <= n*5;i ++){ cin>>x; for(int j = 5;j >= 1;j --){ int num = getsum(a[x][j]-1)+1; if(num > dp[a[x][j]]) dp[a[x][j]] = num,update(a[x][j],num); } } int ans = 0; for(int i = 1;i <= n*5;i ++){ ans = max(dp[i],ans); } cout<<ans<<endl; return 0; }