[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

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$肯定会超
 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 }

 

posted @ 2018-02-03 09:56  Z-Y-Y-S  阅读(220)  评论(0编辑  收藏  举报