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 }

原文地址:https://www.cnblogs.com/wfj2048/p/8001262.html

时间: 2024-10-09 10:18:01

bzoj4600 [Sdoi2016]硬币游戏的相关文章

[SDOI2016]硬币游戏

这道题不难吧,为什么大佬们没有题解呢,一定是dalao们觉得太简单了吧,弄得我好几天才做出来... 很显然,直接按题意模拟即可,求出sg函数,异或和就好了,不知道sg函数的可以自己百度一下...非常神奇的网站 不知道为什么,大佬们都是每次输入n之后再算的sg函数,并且每次算的时候都用的是2的多少次方乘3的多少次方,明明直接求更简单的,效率也并不低... #include<cstdio> #include<cstring> #include<algorithm> usin

1381 硬币游戏

1381 硬币游戏 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题 有一个简单但是很有趣的游戏.在这个游戏中有一个硬币还有一张桌子,这张桌子上有很多平行线(如下图所示).两条相邻平行线之间的距离是1,硬币的半径是R,然后我们来抛硬币到桌子上,抛下之后硬币有时候会和一些直线相交(相切的情况也算是相交),有时候不会. 请你来计算一下抛一次硬币之后,该硬币和直线相交数目的期望. Input 第一行给出一个整数T,表示有T组数据(1<=T<=10000). 第2行到T

bzoj2017[Usaco2009 Nov]硬币游戏*

bzoj2017[Usaco2009 Nov]硬币游戏 题意: 初始时,一个有N枚硬币的堆栈放在地上,每枚硬币都有一个价值.开始玩游戏时,第一个玩家可以从堆顶拿走一枚或两枚硬币.之后每一轮中,当前的玩家至少拿走一枚硬币,至多拿走对手上一次所拿硬币数量的两倍.当没有硬币可拿时,游戏结束. 两个玩家都希望拿到最多钱数的硬币.求第一个玩家最多能拿多少钱.n≤2000. 题解: 首先有dp方程:f[i][j][0]=max(f[i][k][1]+sum(i-k,i)),1≤k≤min(j*2,i),f[

HDU 3537 Mock Turtles型翻硬币游戏

题目大意: 每次可以翻1个或者2个或者3个硬币,但要保证最右边的那个硬币是正面的,直到不能操作为输,这题目还有说因为主人公感情混乱可能描述不清会有重复的硬币说出,所以要去重 这是一个Mock Turtles型翻硬币游戏 下面是对这个类型游戏的讲解 约束条件6:每次可以翻动一个.二个或三个硬币.(Mock Turtles游戏) 初始编号从0开始. 当N==1时,硬币为:正,先手必胜,所以sg[0]=1. 当N==2时,硬币为:反正,先手必赢,先手操作后可能为:反反或正反,方案数为2,所以sg[1]

51Nod - 1381 硬币游戏

51Nod - 1381 硬币游戏 有一个简单但是很有趣的游戏.在这个游戏中有一个硬币还有一张桌子,这张桌子上有很多平行线(如下图所示).两条相邻平行线之间的距离是1,硬币的半径是R,然后我们来抛硬币到桌子上,抛下之后硬币有时候会和一些直线相交(相切的情况也算是相交),有时候不会. 请你来计算一下抛一次硬币之后,该硬币和直线相交数目的期望. Input 第一行给出一个整数T,表示有T组数据(1<=T<=10000). 第2行到T+1,每行给出一个整数R.(0< R <= 10,00

1289 大鱼吃小鱼 1305 Pairwise Sum and Divide 1344 走格子 1347 旋转字符串 1381 硬币游戏

1289 大鱼吃小鱼 有N条鱼每条鱼的位置及大小均不同,他们沿着X轴游动,有的向左,有的向右.游动的速度是一样的,两条鱼相遇大鱼会吃掉小鱼.从左到右给出每条鱼的大小和游动的方向(0表示向左,1表示向右).问足够长的时间之后,能剩下多少条鱼? Input 第1行:1个数N,表示鱼的数量(1 <= N <= 100000). 第2 - N + 1行:每行两个数A[i], B[i],中间用空格分隔,分别表示鱼的大小及游动的方向(1 <= A[i] <= 10^9,B[i] = 0 或 1

博弈——翻硬币游戏

转自:http://blog.sina.com.cn/s/blog_8f06da99010125ol.html 翻硬币游戏 一般的翻硬币游戏的规则是这样的: N 枚硬币排成一排,有的正面朝上,有的反面朝上.我们从左开始对硬币按1 到N 编号. 第一,游戏者根据某些约束翻硬币,但他所翻动的硬币中,最右边那个硬币的必须是从正面翻到反面.例如,只能翻3个硬币的情况,那么第三个硬币必须是从正面翻到反面.如果局面是正正反,那就不能翻硬币了,因为第三个是反的. 第二,谁不能翻谁输. 有这样的结论:局面的SG

软工作业2:硬币游戏——代码的分析与改进

目的: Python 程序阅读理解 学习Python 编码风格指南中译版(Google SOC), 改进Python程序 如何设计游戏规则,使得慈善事业可持续. 地铁口放置硬币箱(初始值500硬币),顾客可取.可放.请设计一组规则,使得该钱箱永远有钱取(尽量符合实际) 参考:地铁口钱箱 作业步骤: step 1: fork 老师的仓库 +硬币游戏:http://git.oschina.net/juking2017/Game.git 将其 fork 到自己的码云仓库. step2:clone 到本

软工作业: (2)硬币游戏—— 代码分析与改进

软工作业: (2)硬币游戏-- 代码分析与改进 一.作业要求 1.Python 程序阅读理解 2.学习Python 编码风格指南中译版(Google SOC)(http://blog.csdn.net/damotiansheng/article/details/43867175),改进Python程序 3.设计游戏规则,使得慈善事业可持续. 地铁口放置硬币箱(初始值500硬币),顾客可取.可放.请设计一组规则,使得该钱箱永远有钱取(尽量符合实际) 注:参考http://www.cnblogs.c