题解:AtCoder AT_awc0021_e Field Watering Plan

【题目来源】

AtCoder:E - Field Watering Plan

【题目描述】

Takahashi will water his field over \(N\) days. Each day, he chooses and executes exactly one of two watering methods.|
高桥将在 \(N\) 天内灌溉他的田地。每一天,他都会从两种灌溉方法中选择并执行其中一种。

Method A is a method that spreads a large amount of water all at once. On a day when this method is chosen, it basically yields a growth amount of \(a\), but because too much water is spread, the soil becomes waterlogged, and the growth amount obtained on the next day is halved (there is no effect on days after that).
方法 A 是一种一次性大量浇水的方法。在选择此方法的那一天,基本可以获得 \(a\) 的生长量,但由于浇水过多,土壤会积水,导致下一天获得的生长量减半(对之后的天数没有影响)。

Method B is a method that carefully spreads a small amount of water. On a day when this method is chosen, it basically yields a growth amount of \(b\). Since there is no burden on the soil, there is no negative effect on the next day.
方法 B 是一种少量谨慎浇水的方法。在选择此方法的那一天,基本可以获得 \(b\) 的生长量。由于对土壤没有负担,因此对下一天没有负面影响。

Specifically, the growth amount for each day is determined by the following rules:
具体来说,每天的生长量由以下规则决定:

  • If Method B was used on the previous day, or if it is the first day (no previous day exists), no halving occurs. If Method A is chosen, the growth amount is \(a\); if Method B is chosen, the growth amount is \(b\).
    如果前一天使用了方法 B,或者这是第一天(没有前一天),则不减半。如果选择方法 A,生长量为 \(a\);如果选择方法 B,生长量为 \(b\)
  • If Method A was used on the previous day, the growth amount for that day is halved. If Method A is chosen, the growth amount is \(\lfloor a / 2 \rfloor\); if Method B is chosen, the growth amount is \(\lfloor b / 2 \rfloor\). Here, \(\lfloor x \rfloor\) denotes the largest integer not exceeding \(x\).
    如果前一天使用了方法 A,则当天的生长量减半。如果选择方法 A,生长量为 \(\lfloor a / 2 \rfloor\);如果选择方法 B,生长量为 \(\lfloor b / 2 \rfloor\)。这里,\(\lfloor x \rfloor\) 表示不超过 \(x\) 的最大整数。

The halving is always applied to the base values \(a\) or \(b\). It does not matter whether the previous day's growth amount was itself halved; the determination is based solely on "whether Method A was used on the previous day." For example, if Method A is chosen for three consecutive days, the growth amounts are \(a\) on the first day, \(\lfloor a/2 \rfloor\) on the second day, and \(\lfloor a/2 \rfloor\) on the third day.
减半始终适用于基准值 \(a\)\(b\)。前一天的生长量是否本身已被减半并不重要;判断仅基于"前一天是否使用了方法 A"。例如,如果连续三天选择方法 A,则第一天的生长量为 \(a\),第二天的生长量为 \(\lfloor a/2 \rfloor\),第三天的生长量为 \(\lfloor a/2 \rfloor\)

Find the maximum possible total growth amount when the watering methods over the \(N\) days are chosen optimally.
求在 \(N\) 天内最优选择灌溉方法时,可能获得的最大总生长量。

【输入】

\(N\) \(a\) \(b\)

  • The first line contains the number of days \(N\), the base growth amount of Method A \(a\), and the base growth amount of Method B \(b\), separated by spaces.

【输出】

Output the maximum possible total growth amount in a single line.

【输入样例】

5 10 3

【输出样例】

32

【核心思想】

  1. 问题分析:高桥在 \(N\) 天内每天选择方法 A(生长量 \(a\),但下一天减半)或方法 B(生长量 \(b\),无影响)。若前一天选了 A,则当天生长量减半(基准值取半)。求 \(N\) 天的最大总生长量。这是一个状态机 DP问题,关键在于发现状态转移具有线性结构,可通过矩阵快速幂\(O(\log N)\) 时间内加速。

  2. 算法选择

    • 状态定义:用两个状态表示前一天的选择:
      • 状态 \(0\):前一天选了 A(当天会减半)
      • 状态 \(1\):前一天选了 B 或是第一天(当天不会减半)
    • Max-Plus 矩阵快速幂:将状态转移建模为 \(2 \times 2\) 矩阵,在max-plus 半环(加法 \(\to\) max,乘法 \(\to\) +)下进行矩阵快速幂
    • 转移矩阵
      • \(M[0][0] = \lfloor a/2 \rfloor\):前一天 A,今天 A,得 \(a/2\)
      • \(M[0][1] = \lfloor b/2 \rfloor\):前一天 A,今天 B,得 \(b/2\)
      • \(M[1][0] = a\):前一天 B,今天 A,得 \(a\)
      • \(M[1][1] = b\):前一天 B,今天 B,得 \(b\)
  3. 关键步骤

    • 读取输入\(N\)(天数)、\(a\)(方法 A 基准生长量)、\(b\)(方法 B 基准生长量)
    • 构建转移矩阵 \(M\)
      • M.c[0][0] = a / 2, M.c[0][1] = b / 2
      • M.c[1][0] = a, M.c[1][1] = b
    • 矩阵快速幂:计算 \(M^n\)。重载 * 运算符为 max-plus 运算:t[i][j] = max(t[i][j], x[i][k] + y[k][j])
    • 提取答案\(M^n\) 中四个元素的最大值即为 \(N\) 天的最大总生长量
    • 输出答案
  4. 时间/空间复杂度

    • 时间复杂度:\(O(\log N)\)。矩阵快速幂将 \(O(N)\) 的递推降至 \(O(\log N)\),每次矩阵乘法为常数时间(\(2 \times 2\) 矩阵)
    • 空间复杂度:\(O(1)\),仅使用若干个 \(2 \times 2\) 矩阵
  5. Max-Plus 矩阵快速幂的核心思想

    • 半环代数结构:在 max-plus 半环中,"加法"是 max,"乘法"是 +。这种结构满足结合律,使得矩阵乘法有意义,快速幂适用
    • 状态压缩:将二维 DP 状态(前天选择、累计生长量)压缩为矩阵形式,转移矩阵的 \(n\) 次幂直接给出 \(n\) 步后的最优值
    • 快速幂加速\(N\) 可达 \(10^{18}\),普通 DP \(O(N)\) 不可行。矩阵快速幂通过对数级次矩阵乘法,将复杂度降至 \(O(\log N)\)
    • 与常规矩阵快速幂的区别:常规矩阵快速幂用于线性递推(如斐波那契),乘法为普通乘加;max-plus 版本用于最优化递推(如最长路径),乘法为 max-plus
    • 适用于具有线性递推结构的最优化问题、图上最长/最短路径计数、状态机最优策略等问题

【解题思路】

【算法标签】

矩阵快速幂

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int INF = 1e18;
int n, a, b, ans = -INF;  // n: 序列长度,a,b: 参数,ans: 最终答案

// 定义2x2矩阵结构体
struct matrix
{
    int c[2][2];  // 2x2矩阵
    matrix()  // 构造函数,初始化为0
    {
        memset(c, 0, sizeof(c));
    }
};

// 矩阵乘法重载,但使用max和+代替乘法和加法
matrix operator*(matrix &x, matrix &y)
{
    matrix t;  // 结果矩阵
    for (int i = 0; i < 2; i++)  // 行
    {
        for (int j = 0; j < 2; j++)  // 列
        {
            for (int k = 0; k < 2; k++)  // 中间维度
            {
                // 特殊的"矩阵乘法":t[i][j] = max(t[i][j], x[i][k] + y[k][j])
                t.c[i][j] = max(t.c[i][j], x.c[i][k] + y.c[k][j]);
            }
        }
    }
    return t;
}

// 矩阵快速幂
matrix qmi(matrix a, int k)
{
    matrix res;  // 单位矩阵(这里初始化为全0,相当于加法的单位元)
    while (k)
    {
        if (k & 1)  // 如果当前二进制位为1
        {
            res = res * a;  // 乘以当前矩阵
        }
        a = a * a;  // 矩阵平方
        k >>= 1;  // 右移一位
    }
    return res;
}

signed main()
{
    cin >> n >> a >> b;  // 读入序列长度和参数a,b

    matrix M;  // 转移矩阵
    // 设置转移矩阵的值
    M.c[0][0] = a / 2;  // 状态0到状态0的转移值
    M.c[0][1] = b / 2;  // 状态0到状态1的转移值
    M.c[1][0] = a;      // 状态1到状态0的转移值
    M.c[1][1] = b;      // 状态1到状态1的转移值

    matrix Mn = qmi(M, n);  // 计算M的n次幂

    // 取结果矩阵中的最大值作为答案
    ans = max({Mn.c[0][0], Mn.c[0][1], Mn.c[1][0], Mn.c[1][1]});

    cout << ans << endl;  // 输出结果
    return 0;
}

【运行结果】

5 10 3
32
posted @ 2026-06-23 11:25  团爸讲算法  阅读(6)  评论(0)    收藏  举报