Dyd's Blog

He who has a strong enough why can bear almost any how.

CF1033G Chip Game

神仙数学题,感觉很考思维

Chip Game

题意

$n$ 堆石子,每堆有 $v_i$ 个,两个人 $A, B$ ,每次分别可以取 $a, b$ 个,且满足 $a, b \in [1, m]$ ,给定 $n, m, v$ 求满足以下条件的 $(a, b)$ 的对数:

  1. $A$ 必胜
  2. $B$ 必胜
  3. 先手必胜
  4. 后手必胜

思路

瞟题解瞟了一下午才看懂

先考虑已知 $(a, b)$ 如何判胜负,不妨让 $a \le b$ ,显然 $v_i$ 和 $v_i \mod (a+ b)$ 等价(赢家一定可以通过 $k$ 此次和对方一起取,在胜负不变的情况下使石子多 $k(a + b)$ 个),我们讨论模后的 $v_i$ :

  1. $v_i \in [0, a)$ ,它没用
  2. $v_i \in [a, b)$ ,它只有 $a$ 可以取,所以只要存在这种情况, $A$ 必胜( $A$ 可以先留着这堆和 $B$ 一起取其它堆石子),它是有利于小者的
  3. $v_i \in [b, 2a)$ ,它对 $A, B$ 都只可取一次,是中性的,注意 $b \ge 2a$ 时不存在这种情况
  4. $v_i \in [\max(2a, b), a + b)$ ,若 $A$ 先手取它,它变成 $2$ ,使 $A$ 必胜;若 $B$ 先手取它,它变成 $1$ ,没用

于是:

  1. 当存在 $2$ 或者存在两个以上 $4$ 时,小者必胜
  2. 否则,若只存在一个 $4$ ,若有小者 + 先手则必胜;否则统计 $3$ 的个数,奇数个小的(同时是后手)胜,偶数个大的胜
  3. 否则,统计 $3$ 的个数,奇数个先手胜,偶数个后手胜

于是枚举 $(a, b)$ 就有了 $O(m^2n)$ 的做法,考虑优化

枚举 $sum = a + b$ ,我们钦定 $B$ 为后手(但不保证 $a, b$ 的大小关系)来计算后手胜的个数,在 $b$ 从小增大的途中,必定存在 $pos$ 使 $b$ 大于 $pos$ 后必败(显然后手、大的是不利的),更具体的,如图:

tu

$A, B$ 必胜显然是对称的,一样多,就是 $\min(pos - l + 1, r - pos)$ 个,那么后手必胜就有 $pos - l + 1 - \min(pos - l + 1, r - pos)$ 个,先手必胜最后用总方案数减即得

那么现在考虑如何如何求 $pos$ ,大力分讨,同样设 $v_i$ 为模后的:

  1. 若 $b < a$ ,则它的胜利条件为以下两者之一:

    • 存在 $v_i \in [b, a)$
    • 存在多于两个 $v_i \in [2b, sum)$

    对于第二个,考虑记录最大值 $mx$ 和次大值 $mx2$ ,那么 $b \le \frac{mx2}{2}$ 都满足第二个,所以 $pos \ge \frac{mx2}{2}$

    对于第一个,即 $b \le v_i < sum - b$ ,得 $b \le v_i$ 和 $b < sum - v_i$ 即 $b \le sum - v_i - 1$ ,所以 $b \le \min(v_i, sum - v_i - 1)$ ,由于存在即可, $pos$ 当取所有 $v_i$ 计算得的最大值

  2. 若 $b = a$ ,统计 $v_i \ge \frac{sum}{2}$ 的个数,偶数数个则可以使 $pos = \frac{sum}{2}$ ;这里结束后,若 $pos < \frac{sum}{2}$ ,说明已经无法在让 $pos$ 变大了(过不去 $b = a = \frac{sum}{2}$ 这个点),直接返回

  3. 若 $b > a$ ,则它的胜利条件必须满足:

    • 所有 $v_i \in [0, a) \cup [b, 2a)$

    我们有 $0 \le v_i < sum - b$ 或 $b \le v_i < 2(sum - b)$ ,化简得 $b \le \max(v_i, sum - v_i - 1)$ , $b \le sum - \frac{v_i}{2} - 1$ ,最后一个条件可以直接带 $mx$ ,第一个只好枚举,由于是必须满足,所以 $pos$ 当取所有计算值的最小值

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <bits/stdc++.h>
using LL = long long;
const int N = 100 + 100;
int n, m, b[N];
LL a[N], ans[4];
int find(int sum)
{
int mx = 0, mx2 = 0, res = 0, cnt = 0;
for (int i = 1; i <= n; ++i)
{
if ((b[i] = a[i] % sum) >= (sum + 1) >> 1) ++cnt;
if (b[i] > mx) mx2 = mx, mx = b[i];
else if (b[i] > mx2) mx2 = b[i];
}
res = mx2 >> 1;
for (int i = 1; i <= n; ++i) res = std::max(res, std::min(b[i], sum - b[i] - 1));
if (!(cnt & 1)) res = sum >> 1;
if (res < (sum >> 1)) return res;
res = sum - (mx >> 1) - 1;
for (int i = 1; i <= n; ++i) res = std::min(res, std::max(b[i], sum - b[i] - 1));
return res;
}
int main()
{
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
for (int s = 2, pos, l, r; s <= (m << 1); ++s)
{
l = std::max(1, s - m), r = std::min(s - 1, m);
pos = std::min(std::max(find(s), l - 1), r);
ans[0] += std::min(pos - l + 1, r - pos);
ans[1] += pos - l + 1 - std::min(pos - l + 1, r - pos);
}
printf("%lld %lld %lld %lld\n", ans[0], ans[0], LL(m) * m - (ans[0] << 1) - ans[1], ans[1]);
return 0;
}