【问题描述】
针⽼师和⼩ C 在玩传统的取⽯⼦游戏,他们有 n 堆⽯⼦,第 i 堆⽯⼦
有 ai 个,两个⼈轮流操作,每次操作需要选择⼀堆⾮空的⽯⼦,然后取⾛
这堆⽯⼦⾥⾄少 1 颗⽯⼦,不能操作的⼈输,⼩ C 先⼿
现在针⽼师得到了⼀次作弊的机会:在游戏开始之前他可以动⽤膜法
删除某 k 堆⽯⼦,要求 k 是 d 的倍数,且 0 ≤ k < n。
针⽼师想知道,他有多少种作弊的⽅案,使得作弊后他⼀定必胜。
你只需要输出⽅案数对 1 0 9 + 7 10^9 + 7 109+7 取模的值。
【输入格式】
第⼀⾏两个正整数 n, d。
第⼆⾏ n 个正整数,第 i 个正整数表⽰ ai
【输出格式】
输出⽅案数对 1 0 9 + 7 10^9 + 7 109+7 取模的值。
【样例输入】
5 2
1 2 3 1 1
【样例输出】
4
【数据范围】
对于前 30% 的数据,有
1
≤
n
≤
20
1 ≤ n ≤ 20
1≤n≤20。
对于前 50% 的数据,有
1
≤
n
≤
50
,
1
≤
a
i
≤
1
0
4
1 ≤ n ≤ 50,1 ≤ a_i ≤ 10^4
1≤n≤50,1≤ai≤104。
另有 20% 的数据,满⾜不同的 ai 最多只有 5 种。
对于 100% 的数据,有
1
≤
n
≤
5
∗
1
0
5
,
1
≤
d
≤
10
,
1
≤
a
i
≤
1
0
6
,
1
≤
∑
i
=
1
n
a
i
≤
1
0
7
。
1 ≤ n ≤ 5 ∗ 10^5,1 ≤ d ≤ 10,1 ≤ ai ≤ 10^6, 1 ≤ ∑_{i=1}^n a_i ≤ 10^7。
1≤n≤5∗105,1≤d≤10,1≤ai≤106,1≤∑i=1nai≤107。
s
o
l
u
t
i
o
n
solution
solution:
朴素
d
p
dp
dp:
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]表示考虑了前
i
i
i个,选了
x
%
d
=
j
x\% d=j
x%d=j个元素,异或和为
k
k
k的方案数。
复杂度
O
(
n
d
max
{
a
i
}
)
O(nd \max\{a_i\})
O(ndmax{ai})
考虑优化,注意到一个重要的性质
∑
i
a
i
≤
1
0
7
\sum_ia_i\le 10^7
∑iai≤107
我们考虑将
{
a
i
}
\{a_i\}
{ai}降序排序,考虑对于第
i
i
i个元素,找到最小的
l
l
l满足
2
l
>
a
i
2^l>a_i
2l>ai
那么我们知道此时的
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]中,如果
k
>
=
2
l
k>=2^l
k>=2l,那么必定是转移不到
0
0
0的,就有一个剪枝了
新复杂度?
记
g
(
l
,
r
)
g(l,r)
g(l,r)表示值在
[
l
,
r
]
[l,r]
[l,r]中的数的个数
那么复杂度为
O
(
d
∗
∑
i
=
1
20
c
n
t
[
2
i
−
1
,
2
i
−
1
]
∗
2
i
)
≈
O
(
d
∑
i
=
1
20
a
i
)
O(d*\sum_{i=1}^{20}cnt[2^{i-1},2^i-1]*2^i)\approx O(d\sum_{i=1}^{20}a_i)
O(d∗∑i=120cnt[2i−1,2i−1]∗2i)≈O(d∑i=120ai)
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x],y = e[i].y;i;i = e[i].n,y = e[i].y)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
int rd()
{
int num = 0;char c = getchar();bool flag = true;
while(c < '0'||c > '9') {if(c == '-') flag = false;c = getchar();}
while(c >= '0' && c <= '9') num = num*10+c-48,c = getchar();
if(flag) return num;else return -num;
}
const int p = 1e9+7;
int n,d,to;
int a[501000],bin[30];
int f[2][15][2010000];
inline void add(int &a,int b){a += b;if(a >= p) a -= p;}
int main()
{
bin[0] = 1;rep(i,1,29) bin[i] = bin[i-1]<<1;
n = rd();d = rd();
rep(i,1,n) a[i] = rd(),to^=a[i];
sort(a+1,a+n+1);reverse(a+1,a+n+1);
int mx = 29;
while(bin[mx-1]>a[1]) mx--;
f[0][0][to] = 1;
rep(i,1,n)
{
if(bin[mx-1]>a[i]) mx--;
rep(j,0,d-1) rep(k,0,bin[mx])
{
f[i&1][j][k] = 0;
add(f[i&1][j][k],f[!(i&1)][j?j-1:j+d-1][k^a[i]]);
add(f[i&1][j][k],f[!(i&1)][j][k]);
}
}
printf("%d\n",f[n&1][0][0]-(n%d==0));
return 0;
}