Description
给定n个石子堆玩nim游戏,求拿走最少的堆使得对手必败
Solution
转化一下题意就是要找到一个最大的子集使得异或和为0,也就是一个最小的子集异或和=全部的异或和
根据线性基的某些性质可以知道这个集合的大小不超过20。
我们设f[i,j]表示i个数字能否组成j,转移可以FWT优化,直接做是
n
l
o
g
2
(
n
)
nlog^2(n)
nlog2(n)的
考虑转化一下,f[i,j]表示至多i个数字能否组成j,那么就可以二分了,于是就是
n
l
o
g
(
n
)
l
o
g
(
l
o
g
(
n
)
)
nlog(n)log(log(n))
nlog(n)log(log(n))
结果还是跑不过直接枚举答案的Orz
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
const int MOD=10007;
const int ny2=(MOD+1)/2;
const int N=524288*2;
int a[N],s;
int f[N],g[22][N];
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void FWT(int *a,int n,int f) {
for (register int i=1;i<n;i<<=1) {
for (register int j=0;j<n;j+=(i<<1)) {
for (register int k=0;k<i;++k) {
int u=a[j+k],v=a[j+k+i];
a[j+k]=(u+v)%MOD,a[j+k+i]=(u+MOD-v)%MOD;
if (f==-1) a[j+k]=a[j+k]*ny2%MOD,a[j+k+i]=a[j+k+i]*ny2%MOD;
}
}
}
}
int main(void) {
freopen("data.in","r",stdin);
freopen("myp.out","w",stdout);
int n=read(),len=524288;
rep(i,1,n) {
int x=read();
f[x]=1; s^=x;
} f[0]=1;
FWT(f,len,1);
rep(i,0,len-1) g[0][i]=1;
rep(j,1,20) rep(i,0,len-1) g[j][i]=g[j-1][i]*f[i]%MOD;
if (!s) return 0&printf("%d\n", n);
int l=1,r=20,ans=0;
for (;l<=r;) {
int mid=(l+r)>>1;
FWT(g[mid],len,-1);
if (g[mid][s]) r=mid-1,ans=n-mid;
else l=mid+1;
}
printf("%d\n", ans);
return 0;
}