【bzoj1264】[AHOI2006]基因匹配Match 树状数组

题目描述

给出两个长度为5n的序列,1~n这n个数在每个序列中都出现了恰好5次。求这两个序列的最长公共子序列。

输入

输入文件中第一行有一个整数N,表示这个星球上某种生物使用了N种不同的碱基,以后将它们编号为1…N的整数。 以下还有两行,每行描述一个DNA序列:包含5N个1…N的整数,且每一个整数在对应的序列中正好出现5次。

输出

输出文件中只有一个整数,即两个DNA序列的最大匹配数目。

样例输入

2
1 1 2 2 1 1 2 1 2 2
1 2 2 2 1 1 2 2 1 1

样例输出

7


题解

树状数组

考虑LCS问题的转化:把$A[i]=B[j]$的数对$(i,j)$当作平面上的点,选出尽可能多的点,使得这些点严格从左下到右上排列。选出点的个数即为答案。

这个转化应该很简单,这里不做过多解释。

那么对于本题,由于每个数只出现$5$次,因此只有$25n$个点。把这些点拿出来,然后直接求最多点个数,即二维偏序。

把所有的点按照纵坐标从小到大排序消掉第二维,使用树状数组维护第一维的前缀最大值即可。此时注意由于第二维的要求是严格的,因此在第二维相同时第一维需要按照从大到小排序一避免产生影响。

时间复杂度$O(25n\log n)$。

#include <cstdio>
#include <algorithm>
#define N 20010
using namespace std;
struct data
{
	int x , y;
	bool operator<(const data &a)const {return y == a.y ? x > a.x : y < a.y;}
}v[N * 25];
int va[N][5] , ta[N] , vb[N][5] , tb[N] , n , f[N * 5];
inline void add(int x , int a)
{
	int i;
	for(i = x ; i <= n ; i += i & -i) f[i] = max(f[i] , a);
}
inline int query(int x)
{
	int i , ans = 0;
	for(i = x ; i ; i -= i & -i) ans = max(ans , f[i]);
	return ans;
}
int main()
{
	int i , j , k , t;
	scanf("%d" , &n) , n *= 5;
	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &t) , va[t][ta[t] ++ ] = i;
	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &t) , vb[t][tb[t] ++ ] = i;
	for(i = 1 ; i <= n / 5 ; i ++ )
		for(j = 0 ; j < 5 ; j ++ )
			for(k = 0 ; k < 5 ; k ++ )
				t = (i - 1) * 25 + j * 5 + k + 1 , v[t].x = va[i][j] , v[t].y = vb[i][k];
	sort(v + 1 , v + n * 5 + 1);
	for(i = 1 ; i <= n * 5 ; i ++ ) add(v[i].x , query(v[i].x - 1) + 1);
	printf("%d\n" , query(n));
	return 0;
}

 

posted @ 2017-10-19 13:44  GXZlegend  阅读(514)  评论(1编辑  收藏  举报