Loading

2.25模拟赛T4题解

前言:

偶遇神秘博弈论,展现人类智慧,拼尽全力无法战胜。

题意:

给定一个长度为 \(n\) 的数列 \(a_i\),Alice和Bob手上分别有一个初始为0的数字(分别记为A,B)。Alice和Bob轮流做出如下操作:

从序列开头或结尾取出一个数 \(x\),让自己手上的数字异或上 \(x\),并把 \(x\) 从序列中删除。

令Alice为先手,Bob为后手,最后谁的数字大,谁就胜。如果先手与后手均采用最优策略,那么谁可以取胜?(或者平局,即两个人手上的数字一样大)

思路:

我们看到平局就非常的不舒服,考虑如何处理平局的情况,设所有数字的异或和为 \(s\),显然的,若最后是平局当且仅当 \(s=0\) ,于是接下来考 \(s\neq0\) 的情况。

因为操作是异或,考虑找到 \(s\) 的二进制最高位,记为 \(2^{pos}\),不难发现,先后手的胜负一定是在这一位上被区分的,同时,因为二进制数的按位操作是独立的,我们就可以把题目中的所有 \(a_i\) 都变成他们本身的二进制第 \(pos\) 位的数去考虑,于是问题就完全转化为:

\(0\leq a_i\leq1\),且一定有奇数个\(1\)的问题。

现在考虑手推几组样例,因为是和题目看起来是博弈论相关,我们考虑分 \(n\) 的奇偶进行讨论。可以发现,若 \(n\) 为偶数,由基本的奇偶性质,要么是所有下标为奇数的 \(a_i\) 上总共有奇数个\(1\),要么是所有下标为偶数的 \(a_i\) 上总共有奇数个\(1\),且两种情况不会同时成立。又注意到(惊人的注意力),先手可以随意控制后手选的下标的奇偶性,于是先手一定可以拿到奇数个\(1\),此时先手必胜。所以现在给出一个对整个解决问题都非常重要的结论:

当数组的长度为偶数,且内含奇数个1时,先手必胜。

为什么是用数组长度来描述这个结论,而不是 \(n\) 呢?且看下文。

现在考虑 \(n\) 为奇数的情况,考虑第一步,发现若两端都是\(0\),那么无论先手选哪一个,都会惊人的化归到上述结论,而此时是后手必胜。所以若两端至少存在一个\(1\),那么为了胜利,先手必须要选择\(1\),否则必败。

那么我们现在假定一开始两端至少存在一个\(1\),且Alice已经选了那个\(1\)。那么现在的情况就变成了数组长度为偶数且含有偶数个\(1\)的情况。

所以对于现在你发现,无论后手接下来选两端的\(0\)还是\(1\),先手都需要选择与后手一样的数字,否则又回到了最开始的那个结论,此时先手必败。那么我们采用贪心,先把两端相等的全部删掉,然后剩下的位置必须形如001111......(即相邻的奇数位置和偶数位置必须相等)先手才有可能胜利,否则后手一定必胜。

那么再更进一步确认先手胜利的条件,分析上述过程,我们令先手取了 \(x+1\)\(1\),而后手取了 \(x\) 次,也就是总共 \(2x+1\) 次,如果排除先手第一次选的\(1\),那么后面每个人分别就选了 \(x\)\(1\),若此时的 \(x\) 是一个奇数,则加上一开始的那一次,先手就得到了偶数个\(1\),败给后手。所以一开始记录序列中\(1\)的个数,如果:

\[cnt_1\equiv 1\pmod{4} \]

且满足 \(n\) 为奇数时的最优策略分析,那么先手必胜,否则全是后手胜。

Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch > '9' || ch < '0'){if(ch == '-'){f = -1;}ch = getchar();}
	while(ch >= '0'&&ch <= '9'){x = x * 10 + ch - 48; ch = getchar();}
	return x * f;
}
const int N = 1e5 + 10;
int n,a[N];

bool check(int l,int r)
{
	while(a[l] == a[r]) ++l,--r;
	if(l > r) return true;
	for(int i = l;i <= r;i += 2)
	{
		if(a[i] != a[i + 1]) return false;
	}
	return true;
}

int main()
{
	int T;
	T = read();
	while(T--)
	{
		int s = 0;
		n = read();
		for(int i = 1;i <= n;++i)
		{
			a[i] = read();
			s ^= a[i];
		}
		if(s == 0)
		{
			puts("Draw");
			continue;
		}
		int pos = 30;
		while(pos >= 0)
		{
			if(s >> pos & 1) break;
			--pos;
		}
		s = 0;
		for(int i = 1;i <= n;++i){
			a[i] = a[i] >> pos & 1;
			s += a[i]; 
		}
		if(n % 2 == 0) {
			puts("Alice");
		}else{
			if(!a[1] && !a[n]) puts("Bob");
			else if(s % 4 == 1){
				if(a[1] && a[n])
				{
					if(check(2,n) || check(1,n - 1)) puts("Alice");
					else puts("Bob");
				}
				else if(a[1])
				{
					if(check(2,n)) puts("Alice");
					else puts("Bob");
				}
				else{
					if(check(1,n - 1)) puts("Alice");
					else puts("Bob");
				}
			}
			else puts("Bob");
		}
	}
	return 0;	
} 
posted @ 2025-03-05 08:25  AxB_Thomas  阅读(21)  评论(0)    收藏  举报
Title