前缀和优化DP

前缀和优化DP

例1: [洛谷P2513 HAOI2009]逆序对数列

\(f[i][j]\) 表示 前i个数字构成逆序对数为 j 的方案总数

\[f[i][j]=\sum_{k=max(0,j-i-1)}^{j}{f[i][k]}\\ 令sum=\sum_{k=max(0,j-i-1)}^{j}{f[i][k]} \]

每次只需要 sum+ $ f[i-1][j] $ 再让$ f[i][j] $ =sum;

然后sum 右移 再 减去 $ f[i-1][j-i-1] $

#include <iostream>
#include <cstdio>
using namespace std;
int n,k;
const int p=10000;
int f[2000][2000],sum;
int main() {
    scanf("%d%d",&n,&k);
    f[1][0]=1;
    for(int i=2;i<=n;sum=0,i++)
        for(int j=0;j<=k;j++) {
            sum=(sum+f[i-1][j])%p;
            f[i][j]=sum;
            if(j-i+1>=0)sum=(sum-f[i-1][j-i+1]+p)%p;
        }
    printf("%d\n",f[n][k]);
    return 0;
}

例2:染色

$ dp[i][j]$ 表示考虑前 i 个位置,\([i-j+1,i]\)中的颜色互不相同,并且\(a_i-j\)与这段区间中的某一个位置颜色相同

我们枚举第\(i+1\)个位置和\([i-j+1,i]\)中的哪一个颜色相同或者全部不同,进行转移

$ dp[i+1][k]+=dp[i][j](1<=k<=j) $

$ dp[i+1][j+1]=dp[i][j]*(m-j) $

这样的话时间复杂度是\(O(N^3)\)

发现第二个转移可以前缀和优化一下,显然 $ dp[i+1][k]$ 可以从 \(dp[i][k-(m-1)]\)转移而来

时间复杂度\(O(N^2)\)

#include <cstdio>
#include <iostream>
using namespace std;
typedef long long LL;
LL f[5010][5010],m,n,s,p;
int main() {
	scanf("%lld%lld%lld",&n,&m,&p);
	f[0][0]=1;
	for(int i=1;i<=n;i++) { 
		s=0;
		for(int j=m-1;j;j--){
			s=(s+f[i-1][j])%p;
			f[i][j]=(s+f[i-1][j-1]*(m-j+1))%p;
		}
	}
	s=0;
	for(int i=1;i<=m;i++)	
		s=(s+f[n][i])%p;
	printf("%lld\n",s);
	return 0;
}

posted @ 2020-08-15 17:41  ke_xin  阅读(290)  评论(0编辑  收藏  举报