1 条题解

  • 0
    @ 2026-4-16 17:50:14

    题意理解

    我们有一个数组 ( a ),长度 ( n ),已经按非递减顺序排好(题目没说,但题意和标程都隐含这一点,因为用了 upper_bound 并假设 ( a_x < a_y < a_z ))。

    Alice 要选三个下标 ( x < y < z ),对应的元素是 ( a_x, a_y, a_z )。她希望她的“红色元素”的 ( R = a_x + a_y + a_z ) 大于 Bob 能得到的“蓝色元素”的最大和。

    游戏规则(根据描述推测):

    • Alice 先选三个位置,把它们染成红色。
    • Bob 再行动,但题里说 Bob 有两种选择:
      1. 选最大元素 ( a_n )(即最后一个元素,因为排好序了)染蓝。
      2. 选当前红色元素中最大的 ( a_z ) 染蓝(这样 Bob 得到的蓝元素和增加 ( a_z ),但红色元素和减少 ( a_z ))。

    两种选择的结果,对红色和蓝色的差距的变化不同。题解里直接给出:

    Bob 会最大化自己的收益,他选
    [ \max(2a_z,\ a_n) ]


    为什么差距改变量是 ( \max(2a_z, a_n) )

    初始时红蓝差距 = 红 - 蓝。在游戏前,Bob 还没选。Bob 有两种操作:

    1. 取 ( a_n ) 染蓝:

      • 蓝元素和增加 ( a_n ),红色不变(因为选的是还没染色的元素)。
      • 差距变化:红蓝差距 = ( R - (B + a_n) ) = 原来的差距 ( (R-B) - a_n )。
        差值的减少量 = ( a_n )。
    2. 取 ( a_z ) 染蓝:

      • 蓝增加 ( a_z ),红减少 ( a_z )(因为 ( a_z ) 原来是红色,现在转蓝)。
      • 差距变化 = ( (R-a_z) - (B + a_z) = R - B - 2a_z )。 差值的减少量 = ( 2a_z )。

    Bob 想让差距减小得多(让他赢),所以他选 ( \max(2a_z, a_n) ) 作为差值减少量。


    Alice 获胜条件

    初始时 Alice 的 R 是 ( a_x + a_y + a_z )。Bob 行动后,差距变为 [ (R - B) - \max(2a_z, a_n) ] 但是 B 初始是 0(未染色时蓝和是 0),所以初始差距就是 ( R )。

    所以 Bob 行动后的差距是: [ R - \max(2a_z, a_n) ]

    Alice 要这个值 > 0 才赢: [ a_x + a_y + a_z > \max(2a_z, a_n) ]


    问题转化

    我们要数三元组 ( (x, y, z) ) 满足:

    1. ( x < y < z )
    2. ( a_x + a_y + a_z > \max(2a_z, a_n) )

    因为数组已排序,且 ( a_x + a_y ) 随 ( x, y ) 增大而增大。对固定的 ( y, z ),要找最小的 ( x ) 使得条件成立,然后所有 ( x ) 到 ( y-1 ) 都成立。


    公式推导

    令: [ M = \max(2a_z, a_n) ] 条件: [ a_x + a_y + a_z > M ] [ a_x > M - a_y - a_z ]

    由于 ( a_x ) 随 ( x ) 增大而增大,我们只需要找第一个 ( a_x ) 大于这个阈值的位置。

    对于固定的 ( j = y ) 和 ( i = z )(代码里 i 是 z 的下标),令: [ t = M - a_i - a_j ] 我们要 ( a_x > t )。

    二分查找第一个 ( a_k > t ) 的位置 ( k )。
    因为数组下标 0..n-1,( a_x ) 对应 ( x < j ),所以在区间 ( [0, j) ) 里找 ( a_k > t ),数量是 ( j - k )。


    代码解析

    int main() {
      int t;
      cin >> t;
      while (t--) {
        int n;
        cin >> n;
        vector<int> a(n);
        for (auto& x : a) cin >> x;
        long long ans = 0;
        for (int i = 0; i < n; ++i) {               // i 是 z 的下标
          for (int j = 0; j < i; ++j) {            // j 是 y 的下标
            int x = max(a[n - 1], 2 * a[i]) - a[i] - a[j];
            int k = upper_bound(a.begin(), a.begin() + j, x) - a.begin();
            ans += j - k;
          }
        }
        cout << ans << '\n';
      }
    }
    

    注意:

    • x 其实对应 ( t = M - a_i - a_j )。
    • upper_bound(a.begin(), a.begin()+j, x) 找第一个 > x 的位置。
    • 因为 ( a_x ) 要 > t,且 x < j,所以 k 是满足 ( a_k > t ) 的最小下标,那么 ( k, k+1, ..., j-1 ) 都满足。
      数量 = ( j - k )。

    复杂度

    • 外层 ( O(n) ),内层 ( O(n) ) 枚举 y,z,每次二分 ( O(\log n) )
      总 ( O(n^2 \log n) ),对于 ( n ) 不大(比如 2000)可行。

    • 可以用双指针优化到 ( O(n^2) ),但这里标程为了清晰没做。


    最终答案

    [ \boxed{\text{计数满足 } a_x + a_y + a_z > \max(2a_z, a_n) \text{ 的三元组数量}} ]
    其中 ( x < y < z ),数组已排序。

    • 1

    信息

    ID
    6546
    时间
    1000ms
    内存
    256MiB
    难度
    3
    标签
    递交数
    1
    已通过
    1
    上传者