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