CF1033G
题意
\(A\) 和 \(B\) 玩游戏。\(A\) 有个数字 \(a\),\(B\) 有个数字 \(b\),有 \(n\) 堆石子,每堆一开始 \(v_i\) 个。两个人轮流走,每次每个人可以选择一个 \(i\) 使得 $v_i\ \geq\ $ 他的数字。不能取得人输。
问 \(1\ \leq\ a,\ b\ \leq\ m\),\(A\) 先手和 \(B\) 先手总共 \(2m^2\) 种局面,\(Alice\) 必胜,\(Bob\) 必胜,先手必胜,后手必胜各有多少个。
\(1\ \leq\ n\ \leq\ 100,\ 1\ \leq\ v_i\ \leq\ 10^{18},\ 1\ \leq\ m\ \leq\ 10^5\)
做法1
考虑给定 \(a,\ b\) 如何判断胜负。
若 \(a\ =\ b\),则 \(\sum_{i\ =\ 1}^n\ \left\lfloor\frac{v_i}{a}\right\rfloor\) 的奇偶性可以确定是先手必胜还是后手必胜。
否则,不妨设 \(a\ <\ b\),对于 \(a\ >\ b\) 同理。
令 \(v'_i\ =\ v_i\ \bmod\ (a\ +\ b)\),则可以证明 \(v\) 与 \(v'\) 是等价的。
将所有 \(v'\) 分成四类。
- \(0\ \leq\ v'_i\ <\ a\) 双方均不能 move。
- \(a\ \leq\ v'_i\ <\ b\) 仅 \(A\) 可以 move。
- \(b\ \leq\ v'_i\ <\ 2a\) 双方可以 move \(1\) 步。
- \(\max(2a,\ b)\ \leq\ v'_i\ <\ a\ +\ b\) \(A\) move \(1\) 步后变成 2.,\(B\) 仅可 move \(1\) 步。
注意 3. 可能不存在。
现在考虑胜负情况:
若存在 2.,则 \(A\) 可以和 \(B\) 抢 3. 和 4.,所以 \(A\) win。
若没有 2.,但有超过 \(2\) 个 4.,则 \(A\) 肯定可以抢到一个 4. 变成 2.,所以 \(A\) win。
若没有 2. 和 4.,则每次抢 3.,若有奇数个 3. 则先手 win,否则后手 win。
若没有 2. 且只有 \(1\) 个 4.,若 \(A\) 先手,可以抢 4.。若 \(B\) 先手,则 \(B\) 必须把 4. 抢了。所以若有奇数个 3. \(A\) win,否则先手 win。
现在考虑计数,只需要算先手 win 和后手 win。每次枚举 \(a,\ b\) 判断是 \(O(nm^2)\)。
考虑枚举 \(X\ =\ a\ +\ b\),然后分类讨论 \(a\)。
若 \(3a\ \leq\ X\),将有 4. 和没 4. 分开算一下,每种情况都是一段连续区间。
若 \(3a\ >\ X\),考虑 \(\max\{v'_i\}\) 与 \(2a\) 的关系即可,每种情况也是一段连续区间。
注意别忘了保证 \(X\ -\ a\ \in\ [1,\ m]\)。
时间复杂度 \(O(nm)\)。
代码
#include <bits/stdc++.h>
#ifdef __WIN32
#define LLFORMAT "I64"
#else
#define LLFORMAT "ll"
#endif
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<long long> a(n);
for (int i = 0; i < n; ++i) cin >> a[i];
long long first = 0, second = 0;
for (long long x = 2; x <= m + m; ++x) {
long long lb = 0, rb = x; array<long long, 2> mx = {0, 0};
lb = x / 3;
while(3 * lb <= x) ++lb;
bool flag = 0;
for (long long v: a) {
long long s = v % x;
if(s * 2 == x) flag = 1;
else if(s * 2 < x) lb = max(lb, s + 1);
else rb = min(rb, s);
for (long long i = 0; i < 2; ++i) if(s > mx[i]) swap(mx[i], s);
}
//3a > x
if(!flag) {
lb = max(max(lb, x - rb), x - m);
rb = x / 2;
if(rb * 2 == x) --rb;
long long nlb = max(lb, (mx[0] + 1) / 2);
while(2 * nlb <= mx[0]) ++nlb;
long long cnt = 0;
for (long long v: a) { long long s = v % x; if(s >= x - nlb && s < 2 * nlb) cnt ^= 1; }
if(cnt) first += max(rb - nlb + 1, 0ll) * 2;
else second += max(rb - nlb + 1, 0ll) * 2;
rb = min(rb, mx[0] / 2);
nlb = max(lb, (mx[1] + 1) / 2);
while(2 * nlb <= mx[1]) ++nlb;
cnt = 0;
for (long long v: a) { long long s = v % x; if(s >= x - nlb && s < 2 * nlb) cnt ^= 1; }
if(!cnt) first += max(rb - nlb + 1, 0ll) * 2;
}
// 3a <= x
lb = max(mx[0] + 1, x - m); rb = x / 3;
if(rb * 2 == x) --rb;
second += max(rb - lb + 1, 0ll) * 2;
lb = max(max(x - mx[0], mx[1] + 1), x - m);
first += max(rb - lb + 1, 0ll) * 2;
if(x % 2 == 0) {
long long y = x / 2, z = 0;
for (long long v: a) z ^= ((v / y) & 1);
if(z) ++first;
else ++second;
}
}
long long foo = ((long long) m * m - first - second) / 2;
cout << foo << ' ' << foo << ' ' << first << ' ' << second << endl;
return 0;
}