Description
Input
Output
Sample Input
Sample Output
7
样例解释:
删去第三堆石子(石子个数为 2的那一堆),剩下 7 堆。
Data Constraint
显然nim游戏中后手会赢的条件是a1^a2^a3…^an=0
所以问题变成了在n个数中选尽量少的数,使得这些数的异或和等于全部数的异或和
dp,设f[i]表示异或和为i时最少个数
转移显然
时间复杂度O(nA)
话说前两档部分分有什么意义
如果在60%的基础上加上一些优化,那么最坏时间还是O(nA)
但根据大量数据实验可以发现,选取的数个数不会超过log A!
结论十分哲♂学,证明如下:
因为所需要表示的数是a中一些数的异或和,该数一定能被a的线性基中的数所表示
而线性基每次加只会多出一种a,所以任意异或出来的数都可以用不超log A种a表示
于是可以设一个很sb的dp:
f[i][j]表示已经选了i个数,异或和是否能为j(0/1)
欸好像是O(n3)
然而并不用慌,根据上面的结论,i只需要选到log A
所以变成了O(n2log A)
然而还是很虚。。。
观察转移,发现每次操作实际上是把f[i]和a的计数数组搞一波异或卷积加到f[i+1],显然可以用FWT优化
每次算完之后把非0位变成1就可以了,表示该方案成立
至于模数。。。
滑稽
#include
#include
#include
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define mod 那位大人的生日
#define len 524287
#define clear(x) x=x!=0?1:0
using namespace std;
int f[20][len+1];
int a[len+1];
int n,i,j,k,l,_2,s;
void tf(int *a)
{
int i,j,k,s1=2,s2=1,s3=262144;
fo(i,1,19)
{
fo(j,0,s3-1)
{
fo(k,0,s2-1)
{
int u=a[s1*j+k],v=a[s1*j+k+s2];
a[s1*j+k]=(u+v)%mod;
a[s1*j+k+s2]=(u-v)%mod;
}
}
s1<<=1;s2<<=1;s3>>=1;
}
}
void utf(int *a)
{
int i,j,k,s1=2,s2=1,s3=262144;
fo(i,1,19)
{
fo(j,0,s3-1)
{
fo(k,0,s2-1)
{
int u=a[s1*j+k],v=a[s1*j+k+s2];
a[s1*j+k]=(long long)(u+v)*_2%mod;
a[s1*j+k+s2]=(long long)(u-v)*_2%mod;
}
}
s1<<=1;s2<<=1;s3>>=1;
}
}
int main()
{
freopen("nim.in","r",stdin);
freopen("nim.out","w",stdout);
_2=(mod+1)/2;
scanf("%d",&n);
fo(i,1,n)
{
scanf("%d",&j);
s^=j;
a[j]=1;
}
tf(a);
f[0][0]=1;
f[1][0]=1;
fo(i,1,18)
{
tf(f[i]);
fo(j,0,len)
f[i][j]=(f[i][j]*a[j])%mod;
utf(f[i]);
fo(j,0,len)
{
clear(f[i][j]);
f[i+1][j]=f[i][j];
}
}
fo(i,0,19)
if (f[i][s])
{
printf("%d\n",n-i);
break;
}
fclose(stdin);
fclose(stdout);
return 0;
}