前缀和优化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;
}