欢迎访问~原文出处——博客园-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; }