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\)的个数,如果:
且满足 \(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;
}

浙公网安备 33010602011771号