专栏文章

题解:AT_agc026_f [AGC026F] Manju Game

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

文章操作

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

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

一、题意简述

# 游戏规则

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

二、核心观察

# 观察 1:游戏的奇偶性质

关键发现:无论如何游戏,最终被选择的位置具有奇偶交替的性质。
证明思路
  • Sugim 第一步选择位置 pp
  • Sigma 必须选择 p1p - 1p+1p + 1(相邻位置)。
  • 之后每步都必须选择与上一步相邻的位置。
  • 因此形成的序列必然是连续的,相邻位置的下标奇偶性不同。
推论
  • 如果 NN 为偶数,必然有一方取所有奇数位置,另一方取所有偶数位置。
  • 如果 NN 为奇数,会在某处发生"转折",改变奇偶归属。

# 观察 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 为奇数的情况,我们需要更精细的分析。
差值序列的构造
定义前缀差值序列 s[i]s[i]
s[i]={s[i1]+a[i]imod2=1s[i1]a[i]imod2=0s[i] = \begin{cases} s[i - 1] + a[i] & i \bmod 2 = 1 \\ s[i - 1] - a[i] & i \bmod 2 = 0 \end{cases}
其中 s[0]=0s[0] = 0
物理意义
  • s[i]s[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
    • 如果 s[i]mnδs[i] - \text{mn} \geq \delta,说明从某个"起始点"到 ii 能达到优势 δ\delta
    • 此时可以选择在 i+1i + 1 处"转折",即改变后续的奇偶归属。
    • 更新 mn=min(mn,s[i+1])\text{mn} = \min(\text{mn}, s[i + 1]),为后续提供更优的起始点。
  3. 最终判定:检查 s[n]mnδ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(N)O(N)
  • 二分答案: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)

七、样例详解

# 样例 1

输入
CPP
5
20 100 10 1 10
分析
  1. N=5N = 5(奇数)。
  2. odd=a[1]+a[3]+a[5]=20+10+10=40\text{odd} = a[1] + a[3] + a[5] = 20 + 10 + 10 = 40
  3. even=a[2]+a[4]=100+1=101\text{even} = a[2] + a[4] = 100 + 1 = 101
  4. 差值序列:
    • s[0]=0s[0] = 0
    • s[1]=0+20=20s[1] = 0 + 20 = 20
    • s[2]=20100=80s[2] = 20 - 100 = -80
    • s[3]=80+10=70s[3] = -80 + 10 = -70
    • s[4]=701=71s[4] = -70 - 1 = -71
    • s[5]=71+10=61s[5] = -71 + 10 = -61
  5. 二分过程:
    • 初始:l=0,r=141l = 0, r = 141
    • 二分得到最大可行 δ=19\delta = 19
  6. 最终答案:
    • Sugim =101+19=120= 101 + 19 = 120
    • Sigma =4019=21= 40 - 19 = 21
输出120 21

# 样例 2

输入
CPP
6
4 5 1 1 4 5
分析
  1. N=6N = 6(偶数)。
  2. odd=4+1+4=9\text{odd} = 4 + 1 + 4 = 9
  3. even=5+1+5=11\text{even} = 5 + 1 + 5 = 11
  4. 直接输出:max(9,11)=11\max(9, 11) = 11min(9,11)=9\min(9, 11) = 9
输出11 9

八、关键要点总结

# 核心技巧

  1. 奇偶性质:利用游戏的相邻限制,发现位置的奇偶交替规律。
  2. 差值序列:将问题转化为优势值的计算。
  3. 二分答案:将最优化问题转化为判定问题。
  4. 贪心 DP:通过维护最优前缀和,判断是否能达到目标优势。

# 易错点

  1. 边界处理:注意 s[i+1]s[i + 1] 的访问,需要预留足够空间。
  2. 奇偶判断:注意区分 NN 的奇偶性。
  3. 二分边界δ\delta 的上界是 odd+even\text{odd} + \text{even},不是 max(odd,even)\max(\text{odd}, \text{even})

# 推广

这道题的思想可以推广到其他相邻限制的博弈问题,核心是:
  • 发现游戏的结构性质(如奇偶性)。
  • 用差值序列建模优势关系。
  • 二分 + 贪心验证可行性。

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

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

作者原创内容:核心观察、算法详解、代码实现、技巧总结、以及其他解题核心部分。

由 AI 辅助 / 生成的内容大致包括但不限于:题解格式、代码细节讲解、题解思路框架、以及样例分析的具体内容(在作者写完过后提交给 AI 检查过一遍拼写等细节问题)。

附录、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 为偶数时"

评论

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

正在加载评论...