4600: [Sdoi2016]硬币游戏

4600: [Sdoi2016]硬币游戏

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 59   Solved: 51
[ Submit][ Status][ Discuss]

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

HINT

Source

By AHdoc命题 鸣谢Loi_DQS上传xiaoyimi提供题面

[ Submit][ Status][ Discuss]

对于每个x,直接考虑暴力枚举p,q转移的代价
对于a,枚举的p的种类不超过a种,每种可行的q的值不超过a / p取下整
那么对于a,枚举p,q的复杂度就可以看成调和级数,即aloga
对于b,同理也是blogb
因为a,b都是logn数量级,所以直接暴力枚举只需要O(nlognloglogn)
记搜一下sg函数即可
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
 
const int maxn = 3E4 + 30;
const int maxm = maxn * 100;
 
int n,m,T,sum,cnt,sg[maxn],bo[maxm],mi2[20],mi3[20];
bool vis[maxn];
 
int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}
 
void Calc(int k)
{
    if (vis[k]) return; vis[k] = 1;
    int a,b,c = k; a = b = 0;
    while (c % 2 == 0) ++a,c /= 2;
    while (c % 3 == 0) ++b,c /= 3;
    if (!a && !b) return;
    stack  s; while (!s.empty()) s.pop(); //s.push(0);
    for (int p = 1; p <= a; p++)
    {
        int now = 0;
        for (int q = 1; p * q <= a && q <= m; q++)
        {
            int Nex = c * mi2[a - p * q] * mi3[b];
            Calc(Nex); now ^= sg[Nex]; s.push(now);
        }
    }
    for (int p = 1; p <= b; p++)
    {
        int now = 0;
        for (int q = 1; p * q <= b && q <= m; q++)
        {
            int Nex = c * mi2[a] * mi3[b - p * q];
            Calc(Nex); now ^= sg[Nex]; s.push(now);
        }
    }
    ++cnt; while (!s.empty()) bo[s.top()] = cnt,s.pop();
    for (int i = 0; ; i++)
        if (bo[i] != cnt) {sg[k] = i; return;}
}
 
void Solve()
{
    n = getint(); m = getint();
    for (int i = 1; i <= n; i++)
    {
        int now = getint();
        if (now) continue;
        Calc(i); sum ^= sg[i];
    }
    puts(sum ? "win" : "lose"); sum = 0;
    for (int i = 1; i <= n; i++) vis[i] = 0;
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    mi2[0] = mi3[0] = 1;
    for (int i = 1; i < 20; i++)
        mi2[i] = mi2[i - 1] * 2,mi3[i] = mi3[i - 1] * 3;
    T = getint(); while (T--)
    Solve();
    return 0;
}

你可能感兴趣的:(博弈论)