【题目大意】
给定n个数和两个长度为n*5的序列,两个序列中的数均有1..n组成,且1..n中每个数恰好出现5次,求两个序列的LCS。
【思路】
预处理每个数字在a[i]中出现的五个位置。f[i]示以a[i]为末尾的最长公共子串(*这样就可以避免讨论交叉)。
依次处理b[i],对于每个b[i]找到a[i]中的五个位置转移,用nowp表示,转移很简单:f[nowp]=max(f[nowp],query(nowp-1)+1),这里需要维护前缀最大值。
才知道前缀最大值可以用BIT来维护。
不过要注意的是,一定要从5 downto 1。为什么呢?由于nowp是升序排列的,如果从最小的开始,那么后来的f转移时可能会把先前求出来的f算进去,然而事实上它们对应着的是同一个b[i]。
所以要从大到小。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 const int MAXN=20000+50; 8 int n; 9 int a[5*MAXN],b[5*MAXN],e[5*MAXN],f[5*MAXN]; 10 //f[i]表示以a[i]为末尾的最长公共子串,这样就可以避免讨论交叉 11 int pos[MAXN][6]; 12 int ans=0; 13 14 int lowbit(int x) 15 { 16 return (x&(-x)); 17 } 18 19 int query(int x) 20 { 21 int ret=0;//这里初值必须设为0而不是-1 22 while (x>0) ret=max(ret,e[x]),x-=lowbit(x); 23 return ret; 24 } 25 26 void update(int x,int delta) 27 { 28 while (x<=5*n) e[x]=max(e[x],delta),x+=lowbit(x); 29 } 30 31 void init() 32 { 33 memset(e,0,sizeof(e)); 34 memset(f,0,sizeof(f)); 35 memset(a,0,sizeof(a)); 36 scanf("%d",&n); 37 for (int i=1;i<=5*n;i++) 38 { 39 scanf("%d",&a[i]); 40 pos[a[i]][++pos[a[i]][0]]=i; 41 } 42 for (int i=1;i<=5*n;i++) scanf("%d",&b[i]); 43 } 44 45 void dp() 46 { 47 for (int i=1;i<=5*n;i++) 48 for (int j=5;j>=1;j--)//这里一定要从后往前 49 { 50 int nowp=pos[b[i]][j]; 51 f[nowp]=max(f[nowp],query(nowp-1)+1); 52 update(nowp,f[nowp]); 53 ans=max(ans,f[nowp]); 54 } 55 printf("%d\n",ans); 56 } 57 58 int main() 59 { 60 init(); 61 dp(); 62 return 0; 63 }
时间: 2024-10-06 18:13:08