前缀和优化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
",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
",s);
	return 0;
}

原文地址:https://www.cnblogs.com/ke-xin/p/13509601.html