Loading

Magnitude (Hard Version)

前言

这下自带每日 \(\rm{D}\) 了, 以后上午做下每日 \(\rm{D}\) 和自己找一些每日 \(\rm{C}\) 来做
下午就复习或者看专题

做这个随便高速应该不用剪枝, 但是还是注意数据检验

  • 定义操作 (约束) 和开销 / 收益, 要求最值化开销 / 收益
    • 最优化问题的瓶颈, 考虑找最优解的性质来处理
    • 考虑操作对答案的影响 (推式子) , 据此对操作进行处理
      • 推导每个元素对答案的贡献 \((\)拆贡献\()\)
      • 推导动态规划
  • \(\rm{dp}\)
    • 先考虑最终答案的表达式 \((\)合法解的构造方案\()\) , 基础上进行 \(\rm{dp}\)
    • 分段法
    • 拿几个维度表示状态, 逐个转移
  • 推导贪心策略的常用方法
    • 反证 + 作差
    • 增量法
    • 交换相邻元素法

思路

题意

给定操作数组 pip_i , 和一个初始为 00 的操作数 kk
一轮操作定义为
按递增顺序处理 pip_i , 每次可以执行下面两个操作中的一个

  • kk+pik \gets k + p_i
  • kk+pik \gets |k + p_i|
    求最终使得 kk 最大的操作方案数

首先考虑 \(k\) 最终的最大值会是多少
不能在值域上 \(\rm{dp}\) , 也开不下

考虑两个操作的本质就是对之前值的变换

  • 新的最大值
    • \((1)\) 操作: 一般的, 上一次留下的最大值对其进行 \((1)\) 操作一定可以得到这一次的最大值候选
    • \((2)\) 操作:
      • 如果上一次留下的最大值进行 \((1)\) 操作之后还是负数, 那么对其进行 \((2)\) 操作更优
      • 上一次留下的最小值对其进行 \((2)\) 操作一定可以得到这一次的最大值候选
  • 新的最小值
    • \((1)\) 操作: 一般的, 上一次留下的最小值对其进行 \((1)\) 操作一定可以得到这一次的最小值

所以每次操作记录最大值最小值, 可以转移到新的操作的最大值, 最小值
从而得到最终的 \(k_{\max}\) , 本质上是中间可能出现的一些值一定不优带来的优化\((\)贪心\()\)

考虑方案数计算
对每个操作动态维护当前的最大值最小值有多少种情况可以得来即可

代码
#include <bits/stdc++.h>
#define int long long

const int MOD = 998244353;
namespace calc {
    int add(int a, int b) { return a + b >= MOD ? a + b - MOD : a + b; }
    int mus(int a, int b) { return a - b < 0 ? a - b + MOD : a - b; }
    int mul(int a, int b) { return (a * b) % MOD; }
    void addon(int &a, int b) { a = add(a, b); }
    void mulon(int &a, int b) { a = mul(a, b); }
} using namespace calc;
const int MAXN = 3e5 + 20;

int n;
int a[MAXN];

signed main() {
    int t; scanf("%lld", &t);
    while (t--) {
        scanf("%lld", &n);
        for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
        
        int mx = 0, mn = 0;
        int pmx = 1, pmn = 1, psm = 1;
        for (int i = 1; i <= n; i++) {
            int nmx = 0, nmn = 0x3f3f3f3f;
            int npmx = 0, npmn = 0, npsm = 0;
            /*新的最大值*/
            nmx = std::max({mx + a[i], std::abs(mx + a[i]), std::abs(mn + a[i])});
            if (mx + a[i] == nmx) addon(npmx, pmx);
            if (std::abs(mx + a[i]) == nmx) addon(npmx, pmx);
            if (std::abs(mn + a[i]) == nmx) {
                addon(npmx, mus(pmn, psm));
            }

            /*新的最小值*/
            nmn = mn + a[i];
            if (mn + a[i] == nmn) addon(npmn, pmn);
            if (mx + a[i] == nmx && mn + a[i] == nmn && nmx == nmn) addon(npsm, psm);
            if (std::abs(mn + a[i]) == nmn) addon(npmn, pmn);
            if (std::abs(mn + a[i]) == nmn && std::abs(mx + a[i]) == nmx && nmx == nmn) addon(npsm, psm);

            mx = nmx, mn = nmn, pmx = npmx, pmn = npmn, psm = npsm;
        }
        printf("%lld\n", pmx);
    }

    return 0;
}

总结

最优化问题的瓶颈, 考虑找最优解的性质来处理

posted @ 2025-03-01 11:14  Yorg  阅读(18)  评论(0)    收藏  举报