首先朴素dp的方程,即$f[i][j]=max(f[i][j-1],f[i-1][j],(a[i]==b[j])*(f[i-1][j-1]+1))$,这中间特殊的转移只在a[i]=b[j]时,而在这道题中与a[i]相等的b[j]只有5个,考虑利用这一性质
先预处理每一种数的位置,在枚举i的同时,用线段树来维护f[i]这个数组,然后对于特殊的5个位置从上一轮转移,并对后面的区间max即可
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 100005 4 #define L (k<<1) 5 #define R (L+1) 6 #define mid (l+r>>1) 7 int n,x,a[N],b[N][11],f[N<<2]; 8 void down(int k){ 9 f[L]=max(f[L],f[k]); 10 f[R]=max(f[R],f[k]); 11 f[k]=0; 12 } 13 void update(int k,int l,int r,int x,int y,int z){ 14 if ((l>y)||(x>r))return; 15 if ((x<=l)&&(r<=y)){ 16 f[k]=max(f[k],z); 17 return; 18 } 19 down(k); 20 update(L,l,mid,x,y,z); 21 update(R,mid+1,r,x,y,z); 22 } 23 int query(int k,int l,int r,int x){ 24 if (!x)return 0; 25 if (l==r)return f[k]; 26 down(k); 27 if (x<=mid)return query(L,l,mid,x); 28 return query(R,mid+1,r,x); 29 } 30 int main(){ 31 scanf("%d",&n); 32 n*=5; 33 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 34 for(int i=1;i<=n;i++){ 35 scanf("%d",&x); 36 b[x][++b[x][0]]=i; 37 } 38 for(int i=1;i<=n/5;i++)b[i][6]=n+1; 39 for(int i=1;i<=n;i++) 40 for(int j=5;j;j--) 41 update(1,1,n,b[a[i]][j],b[a[i]][j+1]-1,query(1,1,n,b[a[i]][j]-1)+1); 42 printf("%d",query(1,1,n,n)); 43 }