BZOJ4990 [Usaco2017 Feb]Why Did the Cow Cross the Road II 动态规划 树状数组

欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - BZOJ4990


题意概括

  有上下两行长度为 n 的数字序列 A 和序列 B,都是 1 到 n 的排列,若 abs(A[i]-B[j])<=4,则 A[i]和 B[j]间可以连一条边。现求在边与边不相交的情况下的最大连边数量。


题解

  我们用dp[i][j]表示枚举到A序列的第i个位置,与B序列的第j个位置匹配,所得到的最大效益,这样显然是要超时的,但是不妨去思考一下。

  dp[i][j]=max(dp[i-1][k](1<=k<=j))

  于是我们又发现两个厉害的东西:

  1. 由于每一个数字连出的边最多只有9种情况( abs(A[i]-B[j])<=4),所以转移的复杂度几乎舍去。

  2. 我们发现其实这个东西可以用线段树来维护最大值(当前树状数组也可以的),那么时间复杂度就降成O(n*9 log n)的了。但是线段树的常数太大,被卡了,所以我们用树状数组就可以了。


 代码

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cmath>
using namespace std;
void read(int &x){
	x=0;
	char ch=getchar();
	while (!('0'<=ch&&ch<='9'))
		ch=getchar();
	while ('0'<=ch&&ch<='9'){
		x=x*10+ch-48;
		ch=getchar();
	}
}
const int N=1e5+5;
int n,a[N],b[N],pos[N],ps[10];
int c[N];
int lb(int x){
	return x&-x;
}
void update(int x,int d){
	for (;x<=n;x+=lb(x))
		c[x]=max(c[x],d);
}
int query(int x){
	int ans=0;
	for (;x>0;x-=lb(x))
		ans=max(ans,c[x]);
	return ans;
}
int main(){
	read(n);
	for (int i=1;i<=n;i++)
		read(a[i]);
	for (int i=1;i<=n;i++)
		read(b[i]),pos[b[i]]=i;
	memset(c,0,sizeof c);
	for (int i=1;i<=n;i++){
		int tot=0;
		for (int j=a[i]-4;j<=a[i]+4;j++)
			if (1<=j&&j<=n)
				ps[++tot]=pos[j];
		sort(ps+1,ps+tot+1);
		for (int j=tot;j>=1;j--)
			update(ps[j],query(ps[j]-1)+1);
	}
	printf("%d",query(n));
	return 0;
}

  

posted @ 2017-08-24 21:03  zzd233  阅读(260)  评论(0编辑  收藏  举报