题目:
给定
n
n
n堆石子,第
i
i
i堆有
a
i
a_i
ai个石子,Alice和Bob两个人轮流操作,每次可以从任意一堆中取走若干或将1堆石子拆成两个非空石子堆,两个人采取最优策略,Alice先手,问谁赢。
(
1
≤
n
≤
1
0
6
,
1
≤
a
i
<
2
31
)
(1 \le n \le 10^6,1 \le a_i < 2^{31})
(1≤n≤106,1≤ai<231)
题解:
这种博弈叫
L
a
s
k
e
r
′
s
N
i
m
Lasker's Nim
Lasker′sNim。
首先每个石堆都是独立的,所以我们可以算出单堆的
S
G
SG
SG函数,然后套用
S
G
SG
SG的和。将一堆中还剩的石子数作为游戏的状态,
S
G
(
[
0
]
)
=
0
,
S
G
(
[
1
]
)
=
1
SG([0])=0,SG([1])=1
SG([0])=0,SG([1])=1,
[
k
]
[k]
[k]的后继状态可以为
[
0
]
,
[
1
]
,
.
.
.
,
[
k
−
1
]
,
[
i
]
+
[
k
−
i
]
(
1
≤
i
≤
k
−
1
)
[0],[1],...,[k-1],[i]+[k-i](1 \le i \le k-1)
[0],[1],...,[k−1],[i]+[k−i](1≤i≤k−1)。求出
0
−
n
0-n
0−n的
S
G
SG
SG的复杂度为
O
(
n
2
)
O(n^2)
O(n2),但这道题石子的值域很大,无法直接求
S
G
SG
SG,考虑小范围进行
S
G
SG
SG打表,找规律。可得规律为:当
n
%
4
=
1
n\%4=1
n%4=1或
n
%
4
=
2
n\%4=2
n%4=2时
S
G
(
[
n
]
)
=
n
SG([n])=n
SG([n])=n;当
n
%
4
=
3
n \% 4=3
n%4=3时
S
G
(
[
n
]
)
=
n
+
1
SG([n])=n+1
SG([n])=n+1;当
n
%
4
=
0
n \% 4=0
n%4=0时
S
G
(
[
n
]
)
=
n
−
1
SG([n])=n-1
SG([n])=n−1。因为有
n
n
n堆,所以将
n
n
n堆的
S
G
SG
SG异或一下,如果为0,Bob赢,否则Alice赢。
复杂度: O ( n ) O(n) O(n)
代码:
内附打表程序
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;
#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=1e5+5;
ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
//打表
int sg[maxn],vis[1005];
void sg_table(int n){
sg[0]=0;sg[1]=1;
for(int i=2;i<=n;i++){
memset(vis,0,sizeof(vis));
for(int j=0;j<=i-1;j++)vis[sg[j]]=1;
for(int j=1;j<=i-1;j++){
vis[sg[j]^sg[i-j]]=1;
}
for(int j=0;;j++){
if(!vis[j]){
sg[i]=j;
break;
}
}
}
for(int i=0;i<=n;i++){
printf("%d %d\n",i,sg[i]);
}
}
//==========================================
int T,n;
int a[maxn];
int main(void){
//freopen("in.txt","r",stdin);
// sg_table(50);
scanf("%d",&T);
while(T--){
scanf("%d",&n);
int ans=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
int cur;
if(a[i]%4==1||a[i]%4==2)cur=a[i];
else if(a[i]%4==3)cur=a[i]+1;
else cur=a[i]-1;
ans^=cur;
}
if(ans==0){
puts("Bob");
}
else{
puts("Alice");
}
}
return 0;
}