1. 程式人生 > >【LOJ】#2067. 「SDOI2016」硬幣遊戲

【LOJ】#2067. 「SDOI2016」硬幣遊戲

main || 當前 ++i oid ++ sg函數 std pre

題解

c一樣的就是一個獨立的遊戲

我們對於2和3的指數
sg[i][j] 表示\(c \cdot 2^i \cdot 3^j\)的棋子,只有這個硬幣是反面,翻轉的硬幣是正面的sg值

枚舉sg函數所有可能的局面,每個後繼局面的sg值,就是所有被翻到背面的硬幣sg值的異或和

我們忽略了反轉當前硬幣前面可能不是全部正面,但是這是正確的,我們可以證明一下

如果一個局面所有背面朝上的棋子sg函數異或起來為\(x\)
\(x!=0\)
那麽我們找到\(x\)最高位是\(2^k\),我們找到一個含有\(2^k\)的硬幣\(a_n\)\(x^a_n < a_n\) 我們選擇這個棋子的後繼局面中等於\(x^a_n\)

的那個即可將局面的sg異或值變成0

\(x = 0\)
如果一個局面行動後的後繼局面中有0,那麽這個局面的值一定不為0
所有可操作的局面sg的值都不為0,且後繼局面中的值與該局面sg值不等
故而無論如何操作,sg值為正

代碼

#include <bits/stdc++.h>
#define enter putchar(‘\n‘)
#define space putchar(‘ ‘)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define MAXN 1000005
#define mo 999999137
#define pb push_back
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
    res = 0;T f = 1;char c = getchar();
    while(c < ‘0‘ || c > ‘9‘) {
        if(c == ‘-‘) f = -1;
        c = getchar();
    }
    while(c >= ‘0‘ && c <= ‘9‘) {
        res = res * 10 + c - ‘0‘;
        c = getchar();
    }
    res *= f;
}
template<class T>
void out(T x) {
    if(x < 0) {x = -x;putchar(‘-‘);}
    if(x >= 10) out(x / 10);
    putchar(‘0‘ + x % 10);
}
int sg[55][55];
bool f[60005];
int N,MAXQ,a[30005];
void Init() {
    memset(sg,0,sizeof(sg));
    sg[0][0] = 0;
    for(int i = 0 ; i <= 50 ; ++i) {
        for(int j = 0 ; j <= 50 ; ++j) {
            if(i == 0 && j == 0) continue;
            memset(f,0,sizeof(f));
            if(i != 0) {
                for(int p = 1 ; p <= i ; ++p) {
                    int t = 0;
                    for(int q = 1 ; q <= min(MAXQ,i / p) ; ++q) {
                        t ^= sg[i - p * q][j];
                        f[t] = 1;
                    }
                }
            }
            if(j != 0) {
                for(int p = 1 ; p <= j ; ++p) {
                    int t = 0;
                    for(int q = 1 ; q <= min(MAXQ,j / p) ; ++q) {
                        t ^= sg[i][j - p * q];
                        f[t] = 1;
                    }
                }
            }
            int p = 0;
            while(f[p]) ++p;
            sg[i][j] = p;
        }
    }
}
void Solve() {
    read(N);read(MAXQ);
    Init();
    int ans = 0;
    for(int i = 1 ; i <= N ; ++i) {
        read(a[i]);
        if(!a[i]) {
            int t = i,c2 = 0,c3 = 0;
            while(t % 2 == 0) t /= 2,++c2;
            while(t % 3 == 0) t /= 3,++c3;
            ans ^= sg[c2][c3];
        }
    }
    if(!ans) puts("lose");
    else puts("win");
}
int main() {
    int T;
    read(T);
    while(T--) {
        Solve();
    }
    return 0;
}

【LOJ】#2067. 「SDOI2016」硬幣遊戲