1. 程式人生 > >[HNOI2014]江南樂

[HNOI2014]江南樂

多少 作者 ons amp 現在 勝利 格式 lse vector

題目描述

小A是一個名副其實的狂熱的回合制遊戲玩家。在獲得了許多回合制遊戲的世界級獎項之後,小A有一天突然想起了他小時候在江南玩過的一個回合制遊戲。

遊戲的規則是這樣的,首先給定一個數F,然後遊戲系統會產生T組遊戲。每一組遊戲包含N堆石子,小A和他的對手輪流操作。每次操作時,操作者先選定一個不小於2的正整數M (M是操作者自行選定的,而且每次操作時可不一樣),然後將任意一堆數量不小於F的石子分成M堆,並且滿足這M堆石子中石子數最多的一堆至多比石子數最少的一堆多1(即分的盡量平均,事實上按照這樣的分石子萬法,選定M和一堆石子後,它分出來的狀態是固定的)。當一個玩家不能操作的時候,也就是當每一堆石子的數量都嚴格小於F時,他就輸掉。(補充:先手從N堆石子中選擇一堆數量不小於F的石子分成M堆後,此時共有N+M-1)堆石子,接下來小A從這N+M-1堆石子中選擇一堆數量不小於F的石子,依此類推。

小A從小就是個有風度的男生,他邀請他的對手作為先手。小A現在想要知道,面對給定的一組遊戲,而且他的對手也和他一樣聰明絕頂的話,究竟誰能夠獲得勝利?

輸入輸出格式

輸入格式:

輸入第一行包含兩個正整數T和F,分別表示遊戲組數與給定的數。 接下來T行,每行第一個數N表示該組遊戲初始狀態下有多少堆石子。之後N個正整數,表示這N堆石子分別有多少個。

輸出格式:

輸出一行,包含T個用空格隔開的0或1的數,其中0代表此時小A(後手)會勝利,而1代表小A的對手(先手)會勝利。

輸入輸出樣例

輸入樣例#1:

4 3
1 1
1 2
1 3
1 5

輸出樣例#1:

0 0 1 1

說明

對於100%的數據,T<100,N<100,F<100000,每堆石子數量<100000。

以上所有數均為正整數。


題解

可以看出這是一個\(Multi\_nim\)遊戲
那麽我們要求出大小為\(x\)的一堆石子的\(SG(x)\)
可以枚舉\(m\),然後大小為\(\frac{x}{m}+1\)的有\(x\%m\)
,大小為\(\frac{x}{m}\)的有\(m-x\%m\)
由於是若幹個相同的遊戲異或
所以只有遊戲個數為奇數時才有貢獻
這樣\(SG(x)=mex(SG(\frac{x}{m})[m-x\%m \mod 2]\oplus SG(\frac{x}{m}+1))[x\%m \mod 2]\)
這樣的復雜度是\(O(n^2)\)
那麽我們考慮怎麽來搞這個題
可以發現要求一個函數的\(SG\)

值需要的就是\(SG(\frac{x}{m})\)\(SG(\frac{x}{m}+1)\)
這類似於整除分塊
所以直接對\(x\)整除分塊即可

代碼

#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 100005 ;
using namespace std ;
inline int read() {
    char c = getchar() ; int x = 0 , w = 1 ;
    while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
    while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
    return x*w ;
}

bool exist[M] ;
int vis[M] ;
int F , n , sg[M] ;

int SG(int x) {
    if(x < F) return 0 ;
    if(exist[x]) return sg[x] ;
    for(int l = 2 , r ; l <= x ; l = r + 1) {
        r = x / (x / l) ;
        for(int m = l ; m <= min(l + 1 , x) ; m ++) {
            int tmp = 0 ;
            if((x % m) % 2) tmp ^= SG(x / m + 1) ;
            if((m - x % m) % 2) tmp ^= SG(x / m) ;
            vis[tmp] = x ;
        }
    }
    for(int i = 0 ; i <= x + 1 ; i ++)
        if(vis[i] != x) {
            sg[x] = i ;
            break ;
        }
    exist[x] = true ;
    return sg[x] ;
}
int main() {
    int Case = read() ; F = read() ;
    while(Case --) {
        n = read() ; int ans = 0 ;
        for(int i = 1 , x ; i <= n ; i ++) {
            x = read() ;
            ans ^= SG(x) ;
        }
        if(!ans) printf("0 ") ;
        else printf("1 ") ;
    }
    return 0 ;
}

[HNOI2014]江南樂