hdu 6078 Wavel Sequence(前缀和优化dp)
题意:
给你a,b两个序列,让你在a,b中找出公共子序列p,满足p1<p2>p3<p4...,问有多少种方案。
题解:
考虑dp[i][j][2],表示a序列选择第i个数,b序列选择第j个数,该数和上一个数的关系是:0为小于,1为大于,的方案数。
那么
dp[i][j][0]=sum{ dp[k][l][1] | x<y,k<i,l<j} 其中x=a[i]=b[j],y=a[k]=a[j]。
dp[i][j][1]=sum{ dp[k][l][0] | x>y,k<i,l<j} 其中x=a[i]=b[j],y=a[k]=a[j]。
这样做显然是O(n4)的。考虑优化。
我们发现对于每一个i,j,都是找一个x从1到i-1,y从1到j-1的二维平面,将这个平面满足条件的值相加。
这里运用一个扫描线的思想,设dp[j][2],表示选择b序列的第j个数,后一维和上面一样。
for (i,1:n) for (j,1:m)。
对于i,表示当前已经考虑了a序列的前i个数了,对于j表示当前正在考虑b序列的第j个数。
那么我在for j的时候顺便把b[j]>x和b[j]<x的dp值累加起来。
当前考虑的是前i个数,那么显然当前的dp值的第一维下标都是小于i的,对于j,都是累加的小于j的下标dp值。
那么这个sum的值就是上面的sum{...}。
然后这样就可以O(n2)了。
1 #include<cstdio> 2 #define F(i,a,b) for(int i=(a);i<=(b);++i) 3 const int N=2007,P=998244353; 4 int t,n,m,dp[N][2],a[N],b[N],ans; 5 int main(){ 6 scanf("%d",&t); 7 while(t--) 8 { 9 scanf("%d%d",&n,&m); 10 F(i,1,n)scanf("%d",a+i); 11 F(i,1,m)scanf("%d",b+i); 12 F(i,1,m)dp[i][0]=dp[i][1]=0; 13 int sum[2];ans=0; 14 F(i,1,n) 15 { 16 sum[0]=sum[1]=0; 17 F(j,1,m) 18 { 19 if(a[i]==b[j]) 20 { 21 dp[j][0]=(dp[j][0]+1)%P; 22 dp[j][0]=(dp[j][0]+sum[1])%P; 23 dp[j][1]=(dp[j][1]+sum[0])%P; 24 } 25 if(a[i]>b[j])sum[0]=(sum[0]+dp[j][0])%P; 26 if(a[i]<b[j])sum[1]=(sum[1]+dp[j][1])%P; 27 } 28 } 29 F(i,1,m)ans=(1ll*ans+dp[i][0]+dp[i][1])%P; 30 printf("%d\n",ans); 31 } 32 return 0; 33 }