Description
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/838e79982568da87aa58b352527012d1.png)
1<=n,A=max(a[i])<=5e5,
Solution
- Nim游戏的基本结论,当石子数异或和为0时先手必败,否则必胜。
- 即要求最多的数异或和为0.
- 反过来求最少的数异或和为所有的数的异或和。
- 假定我们将所有的数丢入线性基,那么至少log个数就可以构成所有的情况,所以答案上界为20.
- 接下来我们直接用fwt,每一次与自己相乘(多项式的乘法)。虽然可能会自己乘上自己,但是这对答案并没有影响,因为这个答案一定不如消去这两次的答案更优。
- 具体来说设f[i]表示能否异或乘i(在自乘了若干次后)。最后查询的是f[s]是否为0。
- 时间复杂度是Alog2A的。
- 但是s是固定的,可以预先推算出其他位置对这个s位置的贡献的系数。
- 有一个结论,位置t贡献系数为(-1)|s&t中1的个数|
- 然后就可以预处理点值表达,每次O(n)自乘,O(n)求IFWT。
- 注意取模(随便模一个质数)。
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define maxn 500005
#define ll long long
#define mo 998244353
using namespace std;
int n,i,j,k,a[maxn],s,lim,A,nm;
ll f[maxn*2],g[maxn*2],ct[maxn*2],sum;
void fwt(ll *a,int sig){
for(int mid=1;mid<lim;mid<<=1)
for(int j=0;j<lim;j+=mid<<1)
for(int k=0;k<mid;k++) {
ll x=a[j+k],y=a[j+k+mid];
a[j+k]=(x+y)%mo;
a[j+k+mid]=(x-y+mo)%mo;
}
}
int main(){
freopen("nim.in","r",stdin);
freopen("nim.out","w",stdout);
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&a[i]),s^=a[i],A=max(A,a[i]);
A=max(A,s);
for(lim=1,nm=0;lim<=A;lim<<=1,nm++);
for(i=1;i<lim;i++) ct[i]=ct[i-(i&-i)]+1;
if (s==0){printf("%d\n",n);return 0;}
for(i=1;i<=n;i++) g[a[i]]++;
fwt(g,1);
memcpy(f,g,sizeof(f));
for(i=1;i<=(int)log2(A);i++){
sum=0;
for(j=0;j<lim;j++) {
sum=(sum+((ct[j&s]&1)?-1:1)*f[j]%mo+mo)%mo;
f[j]=f[j]*g[j]%mo;
}
if (sum>0) {printf("%d\n",n-i);break;}
}
}