题意
给出序列
{ai}
,每次可做一次操作:将选定区间里每个
ai
加一
定义
f(a,m)
为将
{ai}
序列全部变成
m
的最小的操作次数。
询问
题解
第一步
脑补一下,
f(a,m)
就是
{ai}
序列上升区间的高度的和
第二步
必须要意识到
|ai−m|≤k
。不然会一直TLE QAQ
第三步
状态表示dp(第i个数字,高度为j,f值为k)
状态转移
当前数字比前面数字大:
dp(i,j,k)+=∑x>0dp(i−1,j−x,k−x)
当前数字比前面数字不大:
dp(i,j,k)+=∑x≥0dp(i−1,j+x,k)
第三步
上面两个状态转移都可以用前缀和优化,也可以加滚动数组。
代码
#include <cstdio>
#include <algorithm>
#define maxm 1003LL
#define maxk 62LL
#define mod 1000000007LL
int n, m, K;
int f[2][maxk][maxk] = {0}; // scroll array
int s[2][maxk][maxk] = {0};
int ss[2][maxk][maxk] = {0}; // sigma f[ last ][ j+i ][ k+i ] (i > 0)
int main() {
register int i, j, k, l, last, now, ans;
scanf("%d%d%d", &n, &m, &K);
m = std::min(m,K); /// !!!!!!!!!!!!!!
ss[0][m][0] = 1, f[0][m][0] = 1;
for (j = m; j >= 0; j -- ) s[0][j][0] = 1, ss[0][j][0] = 0;
for (i = 1; i <= n+1; i ++ ) {
now = i&1, last = !now;
for (j = m; j >= 0; j -- )
for (k = K; k >= 0; k -- ) {
f[now][j][k] = 0, ss[now][j][k] = 0;
l = std::min(j,k);
f[now][j][k] += (ss[last][j-l][k-l] - ss[last][j][k])%mod ;
f[now][j][k] %= mod;
if (f[now][j][k] < 0) f[now][j][k] += mod;
(f[now][j][k] += s[last][j][k]) %= mod;
ss[now][j][k] = s[now][j][k] = f[now][j][k];
if (j < m) (s[now][j][k] += s[now][j+1][k]) %= mod;
if (j < m && k < K) (ss[now][j][k] += ss[now][j+1][k+1]) %= mod;
}
}
ans = 0;
for (k = 0; k <= K; k ++ ) (ans += f[now][m][k]) %= mod;
printf("%d\n", ans);
//while(1);
return 0;
}