【bzoj1188】分裂游戏

  • 本文为博主原创,未经许可不得转载

题解

可以发现,每个石子互相独立,且位置相同的石子等价。所以我们可以预处理出每个位置的$SG$函数值。

如果令状态$u$表示有一枚石子在第$u$堆的状态,那么它的后继状态就是所有“有一枚石子在$j$,一枚石子在$k$,且$j,k$在$u$的右边”的状态。

也就是说,$SG(u)=mex${$SG(j) xor SG(k)|j,k>u$}

最后,我们求出所有石子的$SG$函数值(每个石子的SG函数值为其初始位置的$SG$函数值)的异或和,判断其是否为$0$,即可求得答案。

对于第一步操作,我们可以按字典序枚举之,再计算进行操作后的先后手必胜情况,来判断这一操作是否合法。

——题解来自王队长《OI中的博弈论》

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include<bits/stdc++.h>
#define FILE "read"
using namespace std;
const int MAXN=(int)1e4,D=50;
int ans[5],a[25],check[MAXN+D],sg[MAXN+D];
namespace INIT{
char buf[1<<15],*fs,*ft;
inline char getc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
inline int read(){
int x=0,f=1; char ch=getc();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getc();}
while(isdigit(ch)) {x=x*10+ch-'0'; ch=getc();}
return x*f;
}
}using namespace INIT;
void solve(){
int n=read();
for(int i=1;i<=n;++i) a[i]=read();
memset(check,0,sizeof(check));
memset(sg,0,sizeof(sg));
memset(ans,0,sizeof(ans));
for(int i=n-1;i;--i){
for(int j=i+1;j<=n;++j)for(int k=j;k<=n;++k)check[sg[j]^sg[k]]=i;
for(int j=0;j<=MAXN;++j)if(check[j]!=i){sg[i]=j;break;}
}
for(int i=1;i<=n;++i)if(a[i]&1)ans[0]^=sg[i];
for(int i=n-1;i;--i)for(int j=n;j>=i+1;--j)for(int k=n;k>=j;--k)
if((ans[0]^sg[i]^sg[j]^sg[k])==0) ans[1]=i,ans[2]=j,ans[3]=k,ans[4]++;
printf("%d %d %d\n%d\n",ans[1]-1,ans[2]-1,ans[3]-1,ans[4]);
}
int main(){
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
for(int T=read();T;--T) solve();
return 0;
}
文章目录
  1. 1. 题解
  2. 2. 参考代码
,