1. 程式人生 > >洛谷 P3235 [HNOI2014]江南樂 解題報告

洛谷 P3235 [HNOI2014]江南樂 解題報告

P3235 [HNOI2014]江南樂

Description

兩人進行 T 輪遊戲,給定引數 F ,每輪給出 N 堆石子,先手和後手輪流選擇石子數大於等於 F 的一堆,將其分成任意(大於1)堆,使得這些堆中石子數最多的和最少的相差不超過1(即儘量均分)。求先手和後手誰必勝。

Input

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

Output

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


預處理每個單一遊戲的\(SG\)

小於\(F\)的置\(0\)必敗

大於\(F\)的列舉拆分的堆數,把分開的用\(SG\)定理求一個異或和。

發現可以用乘除分塊優化,對奇偶性相同的堆數當\(\lfloor\frac{n}{l}\rfloor\)一樣時,答案一樣。

預處理的複雜度\(O(n\sqrt n)\)

注意特判\(SG_1=0\)

直接\(SG\)定理回答詢問就可以了。


Code:

#include <cstdio>
const int N=1e5+1;
int SG[N],T,F,n,is[N];
int hxor(int x,int k)
{
    if(k&1) return x;
    return 0;
}
int main()
{
    scanf("%d%d",&T,&F);
    for(int i=F;i<N;i++)
    {
        for(int l=1,r;l<=i;l=r+1)
        {
            r=i/(i/l);
            is[hxor(SG[i/l],l-i%l)^hxor(SG[i/l+1],i%l)]=i;
            ++l;
            if(l<=r&&l<=i) is[hxor(SG[i/l],l-i%l)^hxor(SG[i/l+1],i%l)]=i;
        }
        for(int j=0;is[j]==i;j++) SG[i]=j+1;
    }
    SG[1]=0;
    while(T--)
    {
        scanf("%d",&n);
        int sg=0;
        for(int x,i=1;i<=n;i++) scanf("%d",&x),sg^=SG[x];
        printf("%d ",sg>0);
    }
    return 0;
}

2018.12.19