1. 程式人生 > >牛客網暑期ACM多校訓練營(第八場) H(Playing games)

牛客網暑期ACM多校訓練營(第八場) H(Playing games)

題目描述 
Niuniu likes playing games. He has n piles of stones. The i-th pile has ai stones. He wants to play with his good friend, UinUin. Niuniu can choose some piles out of the n piles. They will play with the chosen piles of stones. UinUin takes the first move. They take turns removing at least one stone from one chosen pile. The player who removes the last stone from the chosen piles wins the game. Niuniu wants to choose the maximum number of piles so that he can make sure he wins the game. Can you help Niuniu choose the piles?

輸入描述:
The first line contains one integer n (1 ≤ n ≤ 500000), which means the number of piles.
The second line describes the piles, containing n non-negative integers, a1 a2 … an, separated by a space. The integers are less than or equal to 500000.

輸出描述:
Print a single line with one number, which is the maximum number of piles Niuniu can choose to make sure he wins. If Niuniu cannot always win whatever piles he chooses, print 0.

輸入

8
1 9 2 6 0 8 1 7
輸出

7
說明
Niuniu can choose the piles {1,9,6,0,8,1,7} to make sure he wins the game.

題意:共有n堆石子,第 i 堆有a[ i ]個,從中選k堆進行遊戲,然後進行nim遊戲,問後哪的人石子的選多少個必勝,儘可能選多選。

思路:sum=a1^a2^...^an,因此將問題轉化成儘可能少選的使得若干個異或值為sum。然後你可以將數看成一個有18個變元的向量,如果找出18個線性無關的向量便可以表示出sum。
由於 d 維線性無關向量組最多有n個向量,所以我們最多在那些數字裡面取 k個。

利用FWT演算法

程式碼:

#define maxn 2000600
void fwt(int a[],int n)
{
    for(int d=1;d<n;d<<=1)
    {
        for(int m=d<<1,i=0;i<n;i+=m)
        {
            for(int j=0;j<d;++j)
            {
                int x=a[i+j],y=a[i+j+d];
                a[i+j]=x+y,a[i+j+d]=x-y;
            }
        }
    }
}
void ufwt(int a[],int n)
{
    for(int d=1;d<n;d<<=1)
    {
        for(int m=d<<1,i=0;i<n;i+=m)
        {
            for(int j=0;j<d;++j)
            {
                int x=a[i+j],y=a[i+j+d];
                a[i+j]=(x+y)/2,a[i+j+d]=(x-y)/2;
            }
        }
    }
}
int a[maxn],n,b[maxn];
int main()
{
    int n;
    scanf("%d",&n);
    int sum=0;
    a[0]=1;
    for(int i=0;i<n;++i)
    {
        int x;
        scanf("%d",&x);
        b[x]=1;
        sum^=x;
    }
    if(!sum) return 0*printf("%d\n",n);
    int m=(1<<19);
    int ans=0;
    int tim=0;
    fwt(b,m);
    while(!a[sum]&&tim<=18)
    {
        ++tim;
        fwt(a,m);
        for(int i=0;i<m;++i) a[i]=a[i]*b[i];
        ufwt(a,m);
        for(int i=0;i<m;++i) a[i]=(a[i]?1:0);
    }
    if(a[sum]) ans=n-tim;
    printf("%d\n",ans);
    return 0;
}