[博弈论] P10398 『STA - R5』Remove and Decrease Game

posted on 2024-05-19 09:30:45 | under | source

\(a_1\dots a_n\) 升序排列。

  • \(n=1\):显然先手必胜。
  • \(n=2\):此时先拿走 \(a_1\) 的必败,即 \(2\mid a_1\) 时先手必胜。
  • \(n>2\)
    • \(n=2\) 做法的拓展:不难发现,操作过程可以看成直接挪走 \(a_1\dots a_{n-2}\)、依次取走 \(a_{n-1}\)、最后直接挪走 \(a_n\)。即总步骤是 \(a_{n-1}+n-1\),若为奇数先手必胜,称其为“胜方”(反之后手为“胜方”)。

    • hack:\(a_1=1\) 就能 hack 上述做法,称其为特殊堆。

    • 先手为“胜方”:因为 \(a_1<a_2\dots <a_n\),因此只要“胜方”优先使用操作二,那么任意局面都不可能出现特殊堆。因此,先手为“胜方”时一定必胜。

    • 先手为 “败方”:\(a_1=1\) 时先手必然会使用操作一,使得自己逆袭为“胜方”。\(a_1>1\) 时先手怎么操作都无法逆袭了。依次类推,不难发现双方会轮流挪走特殊堆实现逆袭,直到特殊堆不存在。具体地,记 \(cnt\) 为特殊堆操作次数,则 \(2\nmid cnt\) 时先手必胜,反之后手必胜。

    • 小细节:\(cnt\) 只应该计算 \(a_1\dots a_{n-2}\),而不能算 \(a_{n-1}\)。这是因为挪走 \(a_{n-1}\) 后必输无疑。

代码

#include<bits/stdc++.h>
using namespace std;

const int N = 2e5 + 5;
int T, n, a[N];

int main(){
	cin >> T;
	while(T--){
		scanf("%d", &n);
		for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
		sort(a + 1, a + 1 + n);
		if(n == 1) printf("Alice\n");
		else if(n == 2) printf("%s\n", (a[1] & 1) ? "Bob" : "Alice");
		else{
			int cnt = 0;
			for(int i = 1; i <= n - 2; ++i) cnt += (a[i] == i); //a[i]=i时可以进行特殊操作。不用break是因为a[i]!=i则后面都不满足条件 
			if((n - 1 + a[n - 1]) & 1) printf("Alice\n");
			else printf("%s\n", (cnt & 1) ? "Alice" : "Bob");
		}
	}
	return 0;
}
posted @ 2026-01-12 20:07  Zwi  阅读(0)  评论(0)    收藏  举报