题解:
然后就是接下来如何fwt
也就是如何处理bit(x) - bit(y) = bit(k)这个条件。
其实就是子集卷积。
把bit(x)和bit(y)划分成两个集合,然后就是子集卷积的形式。
这里设两个新的数组 A[bit(y)][y], B[bit(x)][x],代表拆出来的相应数组
然后对这两个数组做fwt,得到其点值表示,然后直接在外层枚举x和y的大小然后做卷积即可。
这样说可能很抽象,其实贴出代码就很清楚了
#include <iostream> #include <vector> #include <cstdio> using namespace std; const int MOD = 998244353; typedef long long LL; LL mypow(LL a, LL b){ LL ans = 1; for(; b; b >>= 1) { if(b&1) (ans *= a) %= MOD; (a *= a) %= MOD; } return ans; } LL I2 = mypow(2, MOD-2); const int maxn = (1<<19) + 100; LL a[maxn], b[maxn], A[21][maxn*4], B[21][maxn*4], C[21][maxn*4]; vector<int> Bit[21]; int m; class FWT{ public: void fwt(LL *a, int n){ for(int d = 1; d < n; d <<= 1){ for(int m = d<<1, i = 0; i < n; i += m){ for(int j = 0; j < d; j++){ LL x = a[i+j], y = a[i+j+d]; a[i+j] = x+y; if(a[i+j] >= MOD) a[i+j] -= MOD; a[i+j+d] = x-y; if(a[i+j+d] < 0) a[i+j+d] += MOD; } } } } void ufwt(LL *a, int n){ for(int d = 1; d < n; d <<= 1){ for(int m = d<<1, i = 0; i < n; i += m){ for(int j = 0; j < d; j++){ LL x = a[i+j], y = a[i+j+d]; a[i+j] = (x+y)*I2%MOD; a[i+j+d] = (x-y+MOD)*I2%MOD; } } } } void work(LL *a, LL *b, int n){ fwt(a, n); fwt(b, n); for(int i = 0; i < n; i++) a[i] *= b[i]; ufwt(a, n); } }myfwt; int bit(int x){ int ans = 0; for(int i = 0; i < 19; i++) ans += ((x&(1<<i)) > 0); return ans; } int main() { for(int i = 0; i < (1<<19); i++) Bit[bit(i)].push_back(i); cin>>m; for(int i = 0; i < (1<<m); i++) scanf("%d", &a[i]); for(int i = 0; i < (1<<m); i++) scanf("%d", &b[i]); int L = (1<<m); for(int i = 0; i <= m; i++){ for(auto x : Bit[i]){ if(x >= L) break; A[i][x] = (a[x]*(1<<i))%MOD; B[i][x] = b[x]; } myfwt.fwt(A[i], L); myfwt.fwt(B[i], L); } for(int x = 0; x <= m; x++) for(int y = 0; y <= x; y++) for(int i = 0; i < L; i++) (C[x-y][i] += A[y][i]*B[x][i]) %= MOD; for(int i = 0; i <= m; i++) myfwt.ufwt(C[i], L); LL ans = 0, t = 1; for(int i = 0; i < (1<<m); i++){ (ans += C[bit(i)][i]*t) %= MOD; (t *= 1526) %= MOD; } cout<<ans<<endl; return 0; }