下笔春蚕食叶声。

CF1033G Chip Game 博弈

看的跳蛙题解,只是瞎嘴巴一遍加深印象。

update: 2022.4.14 被人贺了,去掉码头。另外,果然是瞎嘴巴,不公平博弈用SG函数/qd

模拟赛出题人搬来的CF *3500=.=

于是我的一整个晚上就被吃了。

题意

现在有一种隔膜。有 n 堆棋子,MC 和 pigstd 轮流取,MC每次只能取 a 个,pigstd 每次只能取 b 个。

问,对于所有满足 a,b[1,m]a,bZ(a,b) ,假设MC和pigstd都绝对聪明,分别有几种方案满足如下四种情况。

  • MC win
  • pigstd win
  • 先手 win
  • 后手 win

n102,m105 ,每堆棋子数量 1018

题解

因为只有pigstd 真的是人win,所以答案是 0,m2,0,0 。做完了。

对于 (a,b)

  • MC win: 拿 a 的必胜
  • pigstd win: 拿 b 的必胜
  • 先手 win:谁先手谁胜
  • 后手 win:谁后手谁胜

显然四种情况是不会有重叠的,容易发现 MC win 和 pigstd win 的情况是等价的。

那么其实只要考虑先手必胜和后手必胜的方案数,然后就能求出所有答案。


G=(v0,v1,...) 的局面等价于 G=(v0mod(a+b),v1mod(a+b),...) 的局面。好像可以解释成,分成了两个子游戏,因为这两个游戏的赢家先后手不变,所以其中有一个是 0 ,那么总的异或和不为0。
那么我们的 vi 就会减小到 2m 以内了。
然而这个东西并不是公平博弈,所以不能用SG函数。所以上面是错的!
正确的证明:
如果赢家是后手,先手取了堆 vi,如果 vi<(a+b) 就按照他在 G 里下一步的策略取,如果 vi(a+b),就也取 vi
如果赢家是先手,他就先按照自己在 G 里的第一步取即可。


叫起来太麻烦了。。还是 A 每次拿 a ,B 每次 拿 b 好了。

我们现在只考虑先手胜还是后手胜的问题,所以我们不妨设 A 是 小手,B 是大手(也就是 ab

  • 情况1:vi[0,a) 废点。不用管。
  • 情况2:vi[a,b) A 可拿 1 次,B拿不了。
  • 情况3:vi[b,2a) A 可拿 1 次,B拿 1 次。
  • 情况4:vi[max(2a,b),a+b) A 可拿 2 次,B 拿 1 次。

我们注意到每堆最多被一个人拿。

分类讨论。被括起来的是必胜者。

  • (A)存在情况2。
  • 不存在情况2
    • (A)情况 4 个数 2:那么,第一轮里, A 肯定可以和 B 抢到一个情况4,将它转化为情况2。
    • 情况 4 个数 =1
      • 情况3 和情况 4 个数([vib]) 为奇数。
        • (A)A是先手
        • (B)B是先手
      • (A)情况3 和情况 4 个数([vib]) 为偶数。
    • 不存在情况 4。
      • 情况 3 个数([vib])是奇数
        • (A)A是先手
        • (B)B是先手
      • 情况 3 个数([vib])是偶数
        • (B)A是先手
        • (A)B是先手

整合一下。只要考虑先手必胜和后手必胜的 (a,b)

  • 不存在情况2
    • 情况 4 个数 =1
      • (先)[vib] 为奇数
    • 不存在情况 4。
      • (先):情况 3 个数([vib])是奇数
      • (后):情况 3 个数([vib])是偶数

那么我们就可以暴力枚举 O(nm2) 了。然而过不去。。


考虑先手胜利的情况:

  • 不存在 vi[a,b)
  • [vi[max(2a,b),a+b)]1 并且 [vib] 为奇数。

考虑后手胜利的情况:

  • 不存在 vi[a,b)
  • [vi[max(2a,b),a+b)]=0 并且 [vib] 为偶数。

首先枚举 a+b

v 进行排序。

要求 a,b 就处于一个 (vj,vj+1] 的区间内。枚举是哪个区间。(边界区间也要考虑)

然后 [vib] 的奇偶性就可知了。分别考虑先手胜和后手胜的情况。

设最大的 vvn,次大的是 vn1

  • 先手

    [vi[max(2a,b),a+b)]1

    即:max(2a,b)>vn1

    即:必须满足 a>vn12b>vn1 中至少一个

    看起来这个玩意儿需要容斥。但是实际上不用。
    因为 a,b 在一个区间里,所以 b>vn1 的时候 a 一定 >vn1>vn12。所以只需要计算 a>vn12 的情况。

  • 后手

    [vi[max(2a,b),a+b)]=0

    即:max(2a,b)>vn

    即:a>vn2b>vn

    同理。
    因为 a,b 在一个区间里,所以 b>vn 的时候 a 一定 >vn>vn2。所以只需要计算 a>vn2 的情况。

那么我们最后会框出一个 a 的范围 [l,r],然后 b(vj,vj+1mods)

l = max(v[j] % s, mx / 2) + 1, r = min(v[j + 1] % s, m);

注意到我们开始有个设定叫 ab,现在我们想抛弃它,否则会很难写。

那么就是 min(a,b)[l,r],max(a,b)∈∈(vj,vj+1mods)

也就是 min(a,b)l,max(a,b)r

al,bl,ar,br

那么最终 a 的范围就是 [min(l,sr),max(r,sl)]

太困难啦!谢谢 @shight 答了一堆奇奇怪怪的疑。

代码

const int N = 110, M = 2e5 + 10;
int n, m, tmp[N];
ll v[N], ans, ansf, anss;
int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++) scanf("%lld", &v[i]);
	for(int s = 2; s <= (m << 1); s++) {
		tmp[0] = 0; tmp[n + 1] = s - 1;
		for(int i = 1; i <= n; i++) tmp[i] = v[i] % s;
		sort(tmp, tmp + (n + 1) + 1);
		for(int i = 1; i <= n + 1; i++) {
			//a,b在 [tmp[i-1],tmp[i]]之间。 
			//fl: >=b 的个数是否为奇数。
			bool fl = (n + 1 - i) & 1;
			int l = max(tmp[i - 1], tmp[n - 1 + (!fl)] / 2) + 1, r = min(tmp[i], m);
			//是奇数:可能后手必败(先手必胜)。找次大 
			//是偶数:可能先手必败(后手必胜)。找最大 
			(fl ? ansf : anss) += max(0, min(r, s - l) - max(l, s - r) + 1);
		}
	} 
	ans = (1ll * m * m - ansf - anss) >> 1;
	printf("%lld %lld %lld %lld\n", ans, ans, ansf, anss);
	return 0;
}
posted @   ACwisher  阅读(254)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示