[POI2016]Nim z utrudnieniem
Description
A和B两个人玩游戏,一共有m颗石子,A把它们分成了n堆,每堆石子数分别为a[1],a[2],...,a[n],每轮可以选择一堆石子,取掉任意颗石子,但不能不取。谁先不能操作,谁就输了。在游戏开始前,B可以扔掉若干堆石子,但是必须保证扔掉的堆数是d的倍数,且不能扔掉所有石子。A先手,请问B有多少种扔的方式,使得B能够获胜。
Input
第一行包含两个正整数n,d(1<=n<=500000,1<=d<=10)。
第二行包含n个正整数a[1],a[2],...,a[n](1<=a[i]<=1000000)。
本题中m不直接给出,但是保证m<=10000000。
Output
输出一行一个整数,即方案数对10^9+7取模的结果。
Sample Input
5 2
1 3 4 1 2
1 3 4 1 2
Sample Output
2
$f[i][j][k]$表示前i个石堆j为取走的石子堆%d值,k为取走石子的异或值
$f[i][j][k]=f[i-1][j-1][k~xor~a[i]]+f[i-1][j][k]$
复杂度$O(n*maxa*d)$
但可以发现$a[i]$和小于$a[i]$的数异或和不会超过$2*a[i]$
所以按$a$排序,限制$k$的枚举上界
此题卡空间
先把第一维去掉,然后一个一个试数组开多大,因为开到$10*2000000$肯定会超
$f[i][j][k]=f[i-1][j-1][k~xor~a[i]]+f[i-1][j][k]$
复杂度$O(n*maxa*d)$
但可以发现$a[i]$和小于$a[i]$的数异或和不会超过$2*a[i]$
所以按$a$排序,限制$k$的枚举上界
此题卡空间
先把第一维去掉,然后一个一个试数组开多大,因为开到$10*2000000$肯定会超
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 int Mod=1e9+7; 8 int f[10][1050001],g[1050001],n,d,a[500001],lim,s; 9 int main() 10 {int i,j,k; 11 scanf("%d%d",&n,&d); 12 for (i=1;i<=n;i++) 13 { 14 scanf("%d",&a[i]); 15 s^=a[i]; 16 } 17 sort(a+1,a+n+1); 18 f[0][0]=1; 19 for (i=1;i<=n;i++) 20 { 21 lim=1; 22 while (lim<=a[i]) lim*=2; 23 for (j=0;j<lim;j++) 24 { 25 g[j]=f[0][j]+f[d-1][j^a[i]]; 26 if (g[j]>=Mod) g[j]-=Mod; 27 } 28 for (j=d-1;j;j--) 29 { 30 for (k=0;k<lim;k++) 31 { 32 f[j][k]=f[j-1][k^a[i]]+f[j][k]; 33 if (f[j][k]>=Mod) f[j][k]-=Mod; 34 } 35 } 36 for (j=0;j<lim;j++) 37 { 38 f[0][j]=g[j]; 39 } 40 } 41 if (n%d==0) f[0][s]--; 42 if (f[0][s]<0) f[0][s]+=Mod; 43 printf("%d\n",f[0][s]); 44 }