专栏文章

题解:AT_agc026_f [AGC026F] Manju Game

AT_agc026_f题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@min5z2ex
此快照首次捕获于
2025/12/01 21:07
3 个月前
此快照最后确认于
2025/12/01 21:07
3 个月前
查看原文
作者撰写的内容标识:本题解除了提议理解相关内容(这部分由 AI 撰写)均为作者撰写。

一、题意简述

游戏规则

NN 个盒子从左到右排成一排,第 ii 个盒子中有 aia_i 个馒头。Sugim(先手)和 Sigma(后手)进行博弈游戏。
游戏规则
  1. 两人轮流操作,每次操作选择一个未放置棋子的盒子放入自己的棋子。
  2. 相邻限制:必须选择与对手上一次操作相邻的盒子(左右相邻均可)。
  3. 特殊情况
    • 如果没有满足条件的相邻盒子,可以任选一个未放置的盒子。
    • Sugim 的第一次操作可以任选盒子。
  4. 游戏进行 NN 次后结束,每人获得自己棋子所在盒子的所有馒头。
目标:求双方都采用最优策略时,各自能获得多少馒头。

二、核心观察

观察 1:游戏的奇偶性质

关键发现:无论如何游戏,最终被选择的位置具有奇偶交替的性质。
证明思路
  • Sugim 第一步选择位置 pp
  • Sigma 必须选择 p1p - 1p+1p + 1(相邻位置)。
  • 之后每步都必须选择与上一步相邻的位置。
  • 因此形成的序列必然是连续的,相邻位置的下标奇偶性不同。

观察 2:NN 为偶数时的简单性

NN 为偶数时:
  • odd=i{1,3,5,,N}ai\text{odd} = \sum_{i \in \{1, 3, 5, \ldots, N\}} a_ieven=i{2,4,6,,N}ai\text{even} = \sum_{i \in \{2, 4, 6, \ldots, N\}} a_i
  • 由于 Sugim 先手,他可以选择取所有奇数位置或所有偶数位置。
  • 最优策略:Sugim 取 max(odd,even)\max(\text{odd}, \text{even}),Sigma 取 min(odd,even)\min(\text{odd}, \text{even})

三、NN 为奇数时的解法

# 3.1 问题建模

对于 NN 为奇数的情况:
差值序列的构造
定义前缀差值序列 sis_i
si={si1+aiimod2=1si1aiimod2=0s_i = \begin{cases} s_{i-1} + a_i & i \bmod 2 = 1 \\ s_{i-1} - a_i & i \bmod 2 = 0 \end{cases}
其中 s0=0s_0 = 0
物理意义
  • sis_i 表示前 ii 个位置按照"奇数位给 Sugim,偶数位给 Sigma"分配时,Sugim 相对于 Sigma 的优势值。
  • 如果存在"转折点",则某些偶数位置会被 Sugim 获得,奇数位置被 Sigma 获得。

# 3.2 二分答案

目标:找到 Sugim 相对于平均值的最大优势 δ\delta
最终答案:
  • Sugim 得分 =even+δ= \text{even} + \delta
  • Sigma 得分 =oddδ= \text{odd} - \delta
二分范围δ[0,odd+even]\delta \in [0, \text{odd} + \text{even}]
二分策略
  • 如果能达到优势 δ\delta,尝试更大的 δ\delta
  • 否则缩小 δ\delta

# 3.3 Check 函数详解

问题:如何判断是否能让 Sugim 获得至少 δ\delta 的优势?
贪心 DP 思路
CPP
bool check(int delta) {
    int mn = 0;  // 记录可用的最小前缀和
    for (int i = 1; i <= n; i += 2) {
        if (s[i] - mn >= delta)  // 如果在位置 i 能达到优势 delta
            mn = min(mn, s[i + 1]);  // 更新最小前缀,允许在 i+1 处"转折"
    }
    return s[n] - mn >= delta;  // 最终检查是否达到 delta
}
算法原理
  1. 状态定义mn 表示当前可选的最优"起始前缀和"。
  2. 转折点选择
    • 遍历所有奇数位置 ii
    • 如果 simnδs_i - \text{mn} \geq \delta,说明从某个"起始点"到 ii 能达到优势 δ\delta
    • 此时可以选择在 i+1i + 1 处"转折",即改变后续的奇偶归属。
    • 更新 mn=min(mn,si+1)\text{mn} = \min(\text{mn}, s_{i+1}),为后续提供更优的起始点。
  3. 最终判定:检查 snmnδs_n - \text{mn} \geq \delta
为什么这样是正确的?
  • 游戏中可以有多次"转折"(重新选择不相邻的盒子)。
  • 每次转折相当于选择一个新的起始点。
  • 贪心地选择最优起始点(前缀和最小)可以最大化后续优势。
  • DP 本质:dp[i] = true 表示能在位置 ii 达到优势 δ\delta

完整步骤

  1. 输入处理
    • 读入 NN 和数组 a[]a[]
    • 计算 odd\text{odd}even\text{even}
    • 构造差值序列 s[]s[]
  2. NN 为偶数时
    • 如果 Nmod2=0N \bmod 2 = 0,直接输出 max(odd,even)\max(\text{odd}, \text{even})min(odd,even)\min(\text{odd}, \text{even})
  3. NN 为奇数时
    • 二分答案 δ[0,odd+even]\delta \in [0, \text{odd} + \text{even}]
    • 对每个 δ\delta,调用 check(delta) 验证可行性。
    • 找到最大的可行 δ\delta
    • 输出 even+δ\text{even} + \deltaoddδ\text{odd} - \delta

代码实现


CPP
#include<bits/stdc++.h>

using namespace std;

#define endl '\n'
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fclose fclose(stdin), fclose(stdout)
#define debug(x) cerr << #x << " = " << (x) << endl

int n;
vector<int> a, s;
int odd, even;

bool check(int delta) {
    int mn = 0;
    for (int i = 1; i <= n; i += 2) {
        if (s[i] - mn >= delta)
            mn = min(mn, s[i + 1]);
    }
    return s[n] - mn >= delta;
}

void solve() {
    cin >> n;
    a.resize(n + 5);
    s.resize(n + 5);
    odd = even = 0;

    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        if (i & 1) {
            odd += a[i];
            s[i] = s[i - 1] + a[i];
        } else {
            even += a[i];
            s[i] = s[i - 1] - a[i];
        }
    }

    if (!(n & 1)) {
        cout << max(odd, even) << " " << min(odd, even) << endl;
        return;
    }

    int l = 0, r = odd + even, delta = 0;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(mid)) {
            delta = mid;
            l = mid + 1;
        } else {
            r = mid - 1;
        }
    }

    cout << even + delta << " " << odd - delta << endl;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int T = 1;
    //cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}

六、复杂度分析

# 时间复杂度

  • 二分答案:O(logS)O(\log S),其中 S=i=1NaiN×1000=3×108S = \sum_{i = 1}^{N} a_i \leq N \times 1000 = 3 \times 10^8
  • 每次 checkO(N)O(N)
  • 总复杂度:O(NlogS)O(N×28)=O(8.4×106)O(N \log S) \approx O(N \times 28) = O(8.4 \times 10^6)

# 空间复杂度

  • 数组存储:O(N)O(N)
  • 总复杂度:O(N)O(N)

参考题目难度及算法标签(仅代表个人观点)

  • 本题涉及的知识点:博弈论、二分答案、贪心、动态规划。
  • 难度评级:省选 / NOI-(个人观点,认为应该是个中紫)。

附录、Updates

  • 2025/11/14:
    1. 数学公式规范化
      • 移除公式中的中文,使用数学符号表达条件
      • 集合求和使用集合符号:i{1,3,5,,N}ai\sum_{i \in \{1, 3, 5, \ldots, N\}} a_i
      • 条件判断使用数学表达式:imod2=1i \bmod 2 = 1 代替 "if ii 为奇数"
    2. 中英文间距
      • 在所有中文与英文、数字、公式之间添加了半角空格
      • 例如:"有NN个盒子" → "有 NN 个盒子"
    3. 标点符号统一
      • 全文统一使用中文标点符号
      • 确保每个句子都有句号结尾
    4. 术语规范化
      • "奇数情况" → "NN 为奇数时"
      • "偶数情况" → "NN 为偶数时"
  • 2025/11/17:
    1. 精简内容
      • 移除了大多数由AI生成的内容
      • 移除了与解题次要的内容,保留更多的解题思路
  • 2025/11/17:
    1. s[i]s[i] 等公式中的数组下标改为 sis_i

评论

0 条评论,欢迎与作者交流。

正在加载评论...