【bzoj2699】更新 dp
题目描述
对于一个数列A[1..N],一种寻找最大值的方法是:依次枚举A[2]到A[N],如果A[i]比当前的A[1]值要大,那么就令A[1]=A[i],最后A[1]为所求最大值。假设所有数都在范围[1, K]内,按上面的步骤执行,有多少个长度N的数列满足A[1]被更新的次数恰好为P呢?
输入
本题有多组数据。输入第一行一个数T为数据组数,下面T行每行依次三个数N、K和P。
输出
对每组数据输出一行,为方案数模1000000007的值。
样例输入
3
4 3 2
2 3 1
3 4 1
样例输出
6
3
30
题解
dp
设 $f[i][j][k]$ 表示 $i$ 个数,更新次数为 $j$ ,最大值为 $k$ 的方案数。
那么考虑第 $i$ 个数是否对最大值产生更新来进行转移:
当不产生更新时,前面最大值为 $k$ ,第 $i$ 个数的取值范围为 $[1,k]$ ,因此 $f[i][j][k]=f[i-1][j][k]*k$ ;
当产生更新时,前面最大值取值范围为 $[1,k-1]$ ,第 $i$ 个数的取值为 $k$ ,因此 $f[i][j][k]=\sum\limits_{l=1}^{k-1}f[i-1][j-1][l]$ 。
因此使用前缀和 $sum[i][j][k]=\sum\limits_{l=1}^kf[i][j][l]$ 来优化dp转移,即可预处理出所有的dp值。
最后对于每个询问直接输出答案即可。
时间复杂度 $O(npk)$
#include <cstdio> #define mod 1000000007 long long sum[155][155][310]; int main() { int i , j , k , T , x , y , z; for(i = 1 ; i <= 300 ; i ++ ) sum[1][1][i] = i; for(i = 2 ; i <= 150 ; i ++ ) for(j = 1 ; j <= 150 ; j ++ ) for(k = 1 ; k <= 300 ; k ++ ) sum[i][j][k] = (sum[i][j][k - 1] + sum[i - 1][j - 1][k - 1] + (sum[i - 1][j][k] - sum[i - 1][j][k - 1]) * k % mod + mod) % mod; scanf("%d" , &T); while(T -- ) scanf("%d%d%d" , &x , &y , &z) , printf("%lld\n" , sum[x][z + 1][y]); return 0; }