对于任意的 n 元集合上的一个集族对应的集合幂级数 A 和 n 次多项式 F(x)=∑i=0nfixi,可以在 O(2nn2) 时间内计算:
i=0∑nfiAi
其中 Ai 代表 i 个 A 做子集卷积。
考虑一般的子集卷积 exp。在那里我们的做法是按最高位分为 n 组,这样每组只有一个。不妨沿用这个思路。
但是做 exp 的时候,子集个数上面带的 (i!)−1 系数被分组之后的固定顺序自然消去,现在无法自然消去。原来的做法中,Gi 代表前 i 组分别构成的集合幂级数做子集卷积之后得到的集合幂级数。考虑一个暴力的推广,就是在外面枚举 A 的次数 j,然后在 G 中再引入一个 k 代表当前的卷积次数。转移的时候枚举是否往里乘就可以了。
这个非常好理解,但是无法达到 O(2nn2) 的目标复杂度。
考虑一个优化。在暴力做法中,最后的答案是 ∑jGn,j,j,初始值是 G0,j,0=fj⋅j!(由于固定顺序会自然消去 j!,这里要补回来),所以我们不妨将 k 变为差值,即还剩多少次。答案就会变成 ∑jGn,j,0,初始状态是 G0,j,j=fj⋅j!。
inlineintqread(){ registerchar c = getchar(); registerint x = 0, f = 1; while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = (x << 3) + (x << 1) + c - 48; c = getchar(); } return x * f; }
constlonglong mod = 998244353; int n, m, k; vector <int> f[22]; int g[22][1 << 21], ffwt[22][1 << 21], gfwt[22][1 << 21], h[1 << 21];
inlineintAdd(int x, int y){ return (x + y >= mod ? x + y - mod : x + y); }
inlineintMinus(int x, int y){ return (x < y ? x - y + mod : x - y); }
inlinevoidRead(){ n = qread(); m = qread(); k = qread(); for (int i = 1;i <= m;i++) { int x = qread(); for (int j = n - 1;j >= 0;j--) { if ((x >> j) & 1) { f[j].push_back(x); break; } } } }
inlinevoidFWT(int *f, int w){ for (int i = 0;i < w;i++) { for (int j = 0;j < (1 << w);j += (1 << i + 1)) { for (int k = j;k < j + (1 << i);k++) f[k + (1 << i)] = Add(f[k + (1 << i)], f[k]); } } }
inlinevoidIFWT(int *f, int w){ for (int i = 0;i < w;i++) { for (int j = 0;j < (1 << w);j += (1 << i + 1)) { for (int k = j;k < j + (1 << i);k++) f[k + (1 << i)] = Minus(f[k + (1 << i)], f[k]); } } }
inlinevoidSolve(){ for (int i = 0;i <= k;i++) g[i][0] = 1; for (int i = 1;i <= n;i++) { for (int j = 0;j <= i;j++) { for (int k = 0;k < (1 << i);k++) ffwt[j][k] = 0; } for (int j = 0;j < f[i - 1].size();j++) ffwt[__builtin_popcount(f[i - 1][j])][f[i - 1][j]]++; for (int j = 0;j <= i;j++) FWT(ffwt[j], i); for (int j = 0;j <= n - i;j++) { for (int j = 0;j <= i;j++) { for (int k = 0;k < (1 << i);k++) gfwt[j][k] = 0; } for (int k = 0;k < (1 << i);k++) gfwt[__builtin_popcount(k)][k] = Add(gfwt[__builtin_popcount(k)][k], g[j + 1][k]); for (int k = 0;k <= i;k++) FWT(gfwt[k], i); for (int k = 0;k <= i;k++) { for (int t = 0;t < (1 << i);t++) h[t] = 0; for (int l = 0;l <= k;l++) { for (int t = 0;t < (1 << i);t++) h[t] = Add(h[t], 1ll * ffwt[l][t] * gfwt[k - l][t] % mod); } IFWT(h, i); for (int t = 0;t < (1 << i);t++) { if (__builtin_popcount(t) == k) g[j][t] = Add(g[j][t], h[t]); } } } } cout << g[0][(1 << n) - 1] << endl; }
intmain(){ Read(); Solve(); #ifdef CFA_44 while (1); #endif return0; }