题解:P13843 集合幂级数 exp(非素数模数)
题意
给定集合幂级数 \(F(x)\),对于每个 \(S\subseteq \{1,2,\cdots,n\}\),求 \([x^S]\exp(F(x))\),答案对 \(\mathbf{2^{64}}\) 取模。
题解
传统做法是对占位多项式做 \(\exp\),需要求逆元,不适用于本题。
考察 \(\exp(F(x))\) 的泰勒展开:
其中乘法是子集卷积。
考虑组合意义,我们知道 \([x^S]F(x)^k\) 的意义是把 \(S\) 有序地划分成 \(k\) 个集合的方案数(划分出的某个集合 \(T\) 内部的方案数为 \([x^T]F(x)\)),那么 \(\dfrac{F(x)^k}{k!}\) 的意义就是无序地划分成 \(k\) 个集合的方案数。因此 \(\exp\) 的组合意义就是对集合做无序划分的方案数,形式化来说:
这个意义很优美,考虑直接对着这个组合意义 DP:设 \(a_S=[x^S]F(x)\),令 \(f_S\) 表示对 \(S\) 做无序划分的方案数。转移时枚举 \(\min(S)\) 所在的划分出的集合 \(T\):
暴力做是 \(\mathcal{O}(3^n)\) 的。
进一步优化,考虑将 \(S\) 按照 \(\min(S)\) 从大到小分层转移,枚举到 \(\min(S)=i\) 时,当前层的转移可以写作
显然 \(\min(T)>i\) 且 \(\min(S-T)>i\),而上式就是子集卷积的形式,于是我们在每一层做一次子集卷积即可得到当前层的 DP 值。
时间复杂度看似是 \(\mathcal{O}(2^nn^3)\),但是注意到在枚举到 \(i\) 时,全集为 \(U=\{i+1,\cdots,n\}\),因此实际的时间复杂度 \(\mathcal{O}(\sum_{i=0}^{n-1}2^ii^2)=\mathcal{O}(2^nn^2)\)。
代码很好写。
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using pii = pair<int, int>;
const int N = 1 << 20;
template<typename T> T lowbit(T x) { return x & -x; }
template<typename T> void chk_min(T &x, T y) { x = min(x, y); }
template<typename T> void chk_max(T &x, T y) { x = max(x, y); }
int n, pc[N];
ull a[N], f[N];
ull F[N][21], G[N][21], H[N][21];
void OR(ull a[N][21], int n, int tp) {
for (int k = 1; k < 1 << n; k <<= 1)
for (int i = 0; i < 1 << n; i += k << 1) for (int j = 0; j < k; ++j)
for (int p = 0; p <= n; ++p) a[i ^ j ^ k][p] += a[i ^ j][p] * tp;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n;
for (int i = 0; i < 1 << n; ++i) cin >> a[i];
for (int s = 1; s < 1 << n; ++s) pc[s] = pc[s ^ lowbit(s)] + 1;
f[0] = 1, f[1 << n - 1] = a[1 << n - 1];
for (int i = n - 2; ~i; --i) {
int sz = n - 1 - i;
for (int s = 0; s < 1 << sz; ++s) {
fill(F[s], F[s] + sz + 1, 0), F[s][pc[s]] = f[s << i + 1];
fill(G[s], G[s] + sz + 1, 0), G[s][pc[s]] = a[(s << i + 1) ^ (1 << i)];
}
OR(F, sz, 1), OR(G, sz, 1);
for (int s = 0; s < 1 << sz; ++s) {
fill(H[s], H[s] + sz + 1, 0);
for (int i = 0; i <= sz; ++i) for (int j = 0; j <= i; ++j)
H[s][i] += F[s][j] * G[s][i - j];
}
OR(H, sz, -1);
for (int s = 0; s < 1 << sz; ++s) f[(s << i + 1) ^ (1 << i)] = H[s][pc[s]];
}
for (int s = 0; s < 1 << n; ++s) cout << f[s] << ' ';
return 0;
}

浙公网安备 33010602011771号