bzoj4600 [Sdoi2016]硬币游戏

Posted wfj_2048

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4600 [Sdoi2016]硬币游戏相关的知识,希望对你有一定的参考价值。

Description

Alice和Bob现在在玩的游戏,主角是依次编号为1到n的n枚硬币。每一枚硬币都有两面,我们分别称之为正面和反面。一开始的时候,有些硬币是正面向上的,有些是反面朝上的。Alice和Bob将轮流对这些硬币进行翻转操作,且Alice总是先手。具体来说每次玩家可以选择一枚编号为x,要求这枚硬币此刻是反面朝上的。对于编号x来说,我们总可以将x写成x=c*2^a*3^b,其中a和b是非负整数,c是与2,3都互质的非负整数,然后有两种选择第一种,选择整数p,q满足a>=p*q,p>=1且1<=q<=MAXQ,然后同时翻转所有编号为c*2^(a-p*j)*3^b的硬币,其中j=0,1,2,..,q。第二种,选择整数p,q满足b>=p*q,p>=1且1<=q<=MAXQ,然后同时翻转所有编号为c*2^a*3^(b-p*j)的硬币,其中j=0,1,2,..,q。可以发现这个游戏不能不先进行下去,当某位玩家无法继续操作上述操作时,便输掉了游戏。作为先手的Alice,总是希望可以在比赛开始之前就知道自己能否获胜。她知道自己和Bob都是充分聪明的,所以在游戏过程中两人都会最优化自己的策略并尽量保证自己处于不败的情形中

Input

本题有多组测试数据,第一行输入一个整数T,表示总的数据组数。之后给出T组数据
每组数据第一行输入两个整数n,MAXQ
第二行输入n个整数,第i个数表示第i个硬币的初始状态,0表示反面朝上,1表示正面朝上
对于100%的数据1<=n<=30000,1<=MAXQ<=20,t<=100。

Output

输出共有t行。对于每一组数据来说,如果Alice先手必胜,则输出"win"(不包括引号),否则输出"lose"

Sample Input

6
16 14
1 0 0 1 0 0 0 0 1 0 0 0 1 0 1 1
16 14
0 1 0 0 0 1 1 1 1 1 1 0 1 0 0 1
16 11
0 1 0 0 0 1 1 1 0 1 0 0 0 1 0 1
16 12
1 1 1 1 1 1 1 1 0 0 1 1 0 1 1 0
16 4
1 0 0 1 0 0 1 0 0 0 0 1 0 1 1 0
16 20
0 0 0 0 1 0 1 0 0 0 1 0 0 1 0 0

Sample Output

win
lose
win
lose
win
win

 

正解:$SG$函数。

推荐博客:一些翻转硬币的游戏

有一个结论:在硬币游戏中,全局的$SG$函数等于所有需要翻转的硬币的$SG$函数的异或和。

所以我们可以发现,翻转一个硬币的$SG$函数只和它这个位置的$2,3$因数个数有关。

于是设$SG[i][j]$表示位置在$p=c*2^{i}*3^{j}$的硬币需要翻转的$SG$函数,直接枚举所有子状态转移就行了。

 

 1 #include <bits/stdc++.h>
 2 #define il inline
 3 #define RG register
 4 #define ll long long
 5 
 6 using namespace std;
 7 
 8 int SG[20][20],vis[500],Q,n,ans;
 9 
10 il int gi(){
11   RG int x=0,q=1; RG char ch=getchar();
12   while ((ch<0 || ch>9) && ch!=-) ch=getchar();
13   if (ch==-) q=-1,ch=getchar();
14   while (ch>=0 && ch<=9) x=x*10+ch-48,ch=getchar();
15   return q*x;
16 }
17 
18 il void pre(){
19   memset(SG,0,sizeof(SG)); RG int x=0,y=0,z;
20   z=n; while (z>=2) z>>=1,++x; z=n; while (z>=3) z/=3,++y;
21   for (RG int i=0;i<=x;++i)
22     for (RG int j=0;j<=y;++j){
23       memset(vis,0,sizeof(vis));
24       for (RG int p=1;p<=i;++p)
25     for (RG int q=1,k;q<=Q && p*q<=i;++q){
26       for (z=-1,k=1;k<=q;++k) z=(z==-1)?SG[i-p*k][j]:(z^SG[i-p*k][j]);
27       if (z!=-1) vis[z]=1;
28     }
29       for (RG int p=1;p<=j;++p)
30     for (RG int q=1,k;q<=Q && p*q<=j;++q){
31       for (z=-1,k=1;k<=q;++k) z=(z==-1)?SG[i][j-p*k]:(z^SG[i][j-p*k]);
32       if (z!=-1) vis[z]=1;
33     }
34       for (z=0;;++z) if (!vis[z]){ SG[i][j]=z; break; }
35     }
36   return;
37 }
38 
39 il void work(){
40   n=gi(),Q=gi(),ans=0,pre();
41   for (RG int i=1,o,x,k1,k2;i<=n;++i){
42     o=gi(); if (o) continue; k1=k2=0;
43     x=i; while (x%2==0) x>>=1,++k1;
44     x=i; while (x%3==0) x/=3,++k2;
45     ans^=SG[k1][k2];
46   }
47   puts(ans ? "win" : "lose"); return;
48 }
49 
50 int main(){
51 #ifndef ONLINE_JUDGE
52   freopen("coin.in","r",stdin);
53   freopen("coin.out","w",stdout);
54 #endif
55   RG int T=gi();
56   while (T--) work();
57   return 0;
58 }

 

以上是关于bzoj4600 [Sdoi2016]硬币游戏的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ4820[Sdoi2017]硬币游戏 AC自动机+概率DP+高斯消元

[SDOI2016]硬币游戏

BZOJ4515: [Sdoi2016]游戏

BZOJ4515[Sdoi2016]游戏 树链剖分+线段树

BZOJ4515 SDOI2016 游戏

[bzoj4515] [Sdoi2016]游戏