题目描述
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;
}