高维前缀和
众所周知, FWT可以轻松的算出高维前缀和
本题题解:
考虑状压(dp)(题目都说了(2^M)那就状压了)因为(\%c[i])和(&a[i-1])这两个操作都和具体的数值有关
(F[i][j])表示枚举到(i), 第(i)个数填(j)有多少种方案
[F[i][j] = egin{cases} 0~~~~~~~~~~~ j~\%~c[i] ==0\\displaystyle sum_{k&j=0} F[i-1][j]end{cases}
]
这样显然是不行的, 需要一点优化
转移时发现每一个合法的k都是(j igotimes (2^M-1))的子集, 如果能记个子集前缀和那就再好不过了, FWT可以帮我们完成这件事, 一遍FWT_or算出子集和, 给出一份易于背诵的代码吧 戳这里
完整代码
#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
const int P = 1000000000;
template <typename T>
void read(T &x) {
x = 0; bool f = 0;
char c = getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
for (;isdigit(c);c=getchar()) x=x*10+(c^48);
if (f) x=-x;
}
ll ans = 0, n, m;
void FWT_or(ll *f) {
for (int i = 1, p = 2; i < m; i <<= 1, p <<= 1)
for (int j = 0; j < m; j += p)
for (int k = 0; k < i; k++)
(f[i+j+k] += f[j+k]) %= P;
}
const int N = 55;
ll f[N][(1<<15) + 5], a[N];
int main() {
int T; read(T);
while (T--) {
memset(f, 0, sizeof(f));
read(n), read(m); m = 1 << m;
for (int i = 1; i <= n; i++) read(a[i]);
for (int i = 1; i < m; i++)
if (i % a[1]) f[1][i] = 1;
FWT_or(f[1]);
for (int i = 2; i <= n; i++) {
for (int j = 1; j < m; j++) {
if (j % a[i] == 0) continue;
f[i][j] += f[i-1][(m-1)^j];
}
FWT_or(f[i]);
}
cout << f[n][m-1] << endl;
}
return 0;
}