1264: [AHOI2006]基因匹配Match
题目:传送门
简要题意:
给出两个序列。每个序列都由n种不同的数字组成,保证每个序列种每种数字都会出现5次(位置不一定一样),也就是序列长度为5*n啦。
求这两个序列的最长公共子序列。
题解:
假的(nlogn)最长公共子序列算法
本蒟蒻看完题:
woc!大水题,这不是就是动态规划求最长公共子序列吗~
看完数据范围...ORZ那么大!!!最长公共子序列的基础算法要(n^2)...完了...不会...瞎搞???
这么皮,怎么破!
tkj神犇:so easy~~(orz...%%%)
好的,%完师兄...
真正的正解:
本题的突破口其实就在于每种数只出现5次。
那么我们可以先把第一个数列种每种数出现的位置用pos[]记录,然后放到第二个数列。
举个栗子,就拿样例来说:
第一个:1 1 2 2 1 1 2 1 2 2
那么pos[1]=1,2,5,6,8 pos[2]=3,4,7,9,10
然后放回第二个数列:1 2 2 2 1 1 2 2 1 1
用一个新的s[]记录新数列,s[]=8 6 5 2 1,10 9 7 4 3, 10 9 7 4 3...(以此类推)
那么我们只需要求出这个新数列的最长上升子序列就ok啦~(原理十分简单,模拟一下就ok)
这里还有一个小细节需要注意:不难发现,我们更新新数列的时候每种数字的位置都是按倒序放的,这样就可以避免在第二个数列中的一个位置重复选择(重点)。
好啦,这样子时间复杂度就为(nlongn)
但是...是假的...因为要是每种数不止出现五次...那就ORZ吧!
AC代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 #define qread(x)x=read(); 7 using namespace std; 8 inline int read() 9 { 10 int f=1,x=0;char ch; 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} 13 return f*x; 14 } 15 struct node1 16 { 17 int pos[10],id; 18 node1(){id=0;} 19 }a[21000]; 20 struct node2 21 { 22 int pos[10],id; 23 node2(){id=0;} 24 }b[21000]; 25 int s[510000]; 26 int n,N,len; 27 bool cmp(int n1,int n2) 28 { 29 return n1>n2; 30 } 31 int f[510000];//表示长度为i的最长上升子序列的末尾(最小) 32 int erfen(int x) 33 { 34 int l=1,r=len,ans=1; 35 while(l<=r) 36 { 37 int mid=(l+r)/2; 38 if(f[mid]>=s[x]) 39 { 40 ans=mid; 41 r=mid-1; 42 } 43 else l=mid+1; 44 } 45 return ans; 46 } 47 int main() 48 { 49 qread(n); 50 for(int i=1;i<=n*5;i++) 51 { 52 int x;qread(x); 53 a[x].id++; 54 a[x].pos[a[x].id]=i; 55 } 56 for(int i=1;i<=n;i++)sort(a[i].pos+1,a[i].pos+5+1); 57 for(int i=1;i<=n*5;i++) 58 { 59 int x;qread(x); 60 int k; 61 k=a[x].id; 62 for(int j=i*5-4;j<=i*5;j++) 63 { 64 s[j]=a[x].pos[k]; 65 k--; 66 } 67 } 68 N=n*25; 69 f[1]=s[1];len=1; 70 for(int i=2;i<=N;i++) 71 { 72 if(s[i]>f[len]) 73 f[++len]=s[i]; 74 else 75 { 76 int j=erfen(i); 77 f[j]=s[i]; 78 } 79 } 80 printf("%d ",len); 81 return 0; 82 }