[CF Gym 100372C] Sergey and array

题意

给出序列 {ai} ,每次可做一次操作:将选定区间里每个 ai 加一
定义 f(a,m) 为将 {ai} 序列全部变成 m 的最小的操作次数。
询问f(a,m)k {ai} 的个数

题解

第一步
脑补一下, f(a,m) 就是 {ai} 序列上升区间的高度的和
第二步
必须要意识到 |aim|k 。不然会一直TLE QAQ
第三步
状态表示dp(第i个数字,高度为j,f值为k)
状态转移
  当前数字比前面数字大:

dp(i,j,k)+=x>0dp(i1,jx,kx)

  当前数字比前面数字不大:
dp(i,j,k)+=x0dp(i1,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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值