bzoj4600 [Sdoi2016]硬币游戏

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4600

【题解】

显然可以发现对于每个c,互相独立。

博弈问题,考虑sg函数。

预处理出每个数的2的因数个数t2[i],3的因数个数t3[i]

对于这一次能翻的硬币,记忆化搞搞sg值,然后异或起来即可。

sg函数非常神奇啊qwq

这题你进去会发现 一个状态的后继状态是由好几个硬币构成的,那么把他们异或起来就行了!

# include <stdio.h>
# include <string.h>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 3e4 + 10, N = 20;
const int mod = 1e9+7;

# define RG register
# define ST static

int n, m, t2[M], t3[M]; 

int sg[N][N];
bool vis[N][N]; 
inline int getsg(int x, int y) {
    if(vis[x][y]) return sg[x][y];
    vis[x][y] = 1;
    int SG; 
    bool g[62]; memset(g, 0, sizeof g);  
    for (int q=1; q<=m; ++q) {
        for (int p=1; p*q<=x; ++p) {
            int s = 0; bool hv = 0;
            for (int j=0; j<=q; ++j) {
                if(!hv) hv = 1; 
                s = s ^ getsg(x-p*j, y);
            }
            if(hv) g[s] = 1; 
        }
    }
    for (int q=1; q<=m; ++q) {
        for (int p=1; p*q<=y; ++p) {
            int s = 0; bool hv = 0;
            for (int j=0; j<=q; ++j) {
                if(!hv) hv = 1; 
                s = s ^ getsg(x, y-p*j);
            }
            if(hv) g[s] = 1; 
        }
    }    
    for (int i=0; i<=60; ++i) 
        if(!g[i]) {
            SG = i;
            break;
        }
    return sg[x][y] = SG; 
}

inline void sol() {
    memset(vis, 0, sizeof vis);
    memset(sg, 0, sizeof sg); 
    scanf("%d%d", &n, &m);
    int ans = 0;
    for (int i=1, t; i<=n; ++i) {
        scanf("%d", &t);
        if(t) continue;
        ans ^= getsg(t2[i], t3[i]); 
    }
    if(ans) puts("win");
    else puts("lose");
}

int main() {
    for (int i=1; i<=30000; ++i) {
        int t = i;
        while(t%2 == 0) t2[i] ++, t/=2;
        while(t%3 == 0) t3[i] ++, t/=3;
    }
    int T; scanf("%d", &T);
    while(T--) sol(); 
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/galaxies/p/bzoj4600.html