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'\) 分成四类。

  1. \(0\ \leq\ v'_i\ <\ a\) 双方均不能 move。
  2. \(a\ \leq\ v'_i\ <\ b\)\(A\) 可以 move。
  3. \(b\ \leq\ v'_i\ <\ 2a\) 双方可以 move \(1\) 步。
  4. \(\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;
}
posted @ 2018-10-12 16:50  King_George  阅读(407)  评论(0编辑  收藏  举报