[USACO17FEB]Why Did the Cow Cross the Road II P
树状数组·特殊的Dp更新方式
题解:
类似最长公共子序列的方程:
f[i][j]=max(f[i−1][j],f[i][j−1])
f[i][j]=max(f[i][j],f[i−1][j−1]+1) 当i、j可以匹配
我们发现+1的位置很少,想办法只处理这些有贡献的。
也就是要快速推知f[i-1][j-1]。
我们把转移等价为:
f[i−1][j]、f[i−1][j−1]+x(x=0or1)、minf[i][k](k<j)
我们把状态压掉第一维变成f[j],然后一行一行的来考虑。
当推下一行的时候,f[]可以直接作为下一行的初始值,这相当于对每个点都做了转移1。
转移3相当于一个前缀最小值,直接用BIT维护f[],用的时候直接查而不是一个一个推。
转移2个数很少,因此先把所有要走转移2的f[j-1]用bit查出来(注意这时bit中还是上一行的状态,满足从f[i-1][j-1]转移),然后再+1更新bit中的f[j]即可(这时bit中才是这一行的状态)。
根据前文“无为而治”转移1,没有+1的状态不用动它,随着i的增加它们相当于在向下传递。
Code:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 200005;
int n,a[N],b[N],p[N],f[N];
struct BIT{
int c[N];
void add(int x,int d){ while(x<N){ c[x]=max(c[x],d); x+=x&-x; } }
int qmx(int x){ int ans=0; while(x){ ans=max(ans,c[x]); x-=x&-x; } return ans; }
} bit;
int main(){
// freopen("a.in","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",a+i);
for(int i=1;i<=n;i++) scanf("%d",b+i), p[b[i]]=i;
for(int i=1;i<=n;i++){
for(int j=max(1,a[i]-4);j<=min(n,a[i]+4);j++) f[p[j]]=bit.qmx(p[j]-1);
for(int j=max(1,a[i]-4);j<=min(n,a[i]+4);j++) bit.add(p[j],f[p[j]]+1);
}
printf("%d\n",bit.qmx(n));
}