P12230 集合幂级数 exp
怎么什么乱七八糟的东西都能丢到
不过盯着这个泰勒展开看你可以瞪出他的组合意义。套用 cxy 在 APIO 2025 的讲课 PPT 上的话说,将
能够理解的是我们不能选择空集,否则方案数是不是就无穷多个了,即钦定
一般来说,有两种
算法一
这个算法要求
考虑子集卷积的类似套路。添加辅助元
定义这个幂级数的
其中
两维独立,固定
现在对于每个不同的
你当然可以拉 但我们有
设
求导可得
直接递推便是
求
:::info[代码]
#include <bits/stdc++.h>
#define F(i, a, b) for (int i = (a); i <= (b); ++i)
#define dF(i, a, b) for (int i = (a); i >= (b); --i)
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int N = 21, K = 1 << N;
const int P = 998244353;
int n, pcnt[K], f[N][K], g[N][K];
int qPow(int x, int y) {
int res = 1;
for (; y; x = 1ll * x * x % P, y >>= 1)
if (y & 1) res = 1ll * res * x % P;
return res;
}
void Add(int &x, int y) { x += y; if (x >= P) x -= P; }
void FMT(int *f, int tp) {
F(i, 0, n - 1) F(j, 0, (1 << n) - 1)
if (j >> i & 1)
if (tp > 0) Add(f[j], f[j ^ 1 << i]);
else Add(f[j], P - f[j ^ 1 << i]);
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
F(i, 1, (1 << n) - 1) pcnt[i] = pcnt[i ^ (i & -i)] + 1;
F(i, 0, (1 << n) - 1) cin >> f[pcnt[i]][i];
F(i, 0, n) FMT(f[i], 1);
F(i, 0, (1 << n) - 1) g[0][i] = 1;
F(i, 1, n) {
F(j, 1, i) F(k, 0, (1 << n) - 1)
Add(g[i][k], 1ll * g[i - j][k] * f[j][k] % P * j % P);
int iv = qPow(i, P - 2);
F(j, 0, (1 << n) - 1) g[i][j] = 1ll * g[i][j] * iv % P;
}
F(i, 0, n) FMT(g[i], -1);
F(i, 0, (1 << n) - 1) cout << g[pcnt[i]][i] << " ";
return 0;
}
:::
算法二
这个算法不需要用到逆元,可以通过加强版。
对着组合意义做有一个简单做法。由于划分集合无序,不妨钦定一个枚举顺序。设
考虑从小到大枚举
这个东西每次做子集卷积就可以了。因为实际上是枚举
时间复杂度是
:::info[代码]
#include <bits/stdc++.h>
#define F(i, a, b) for (int i = (a); i <= (b); ++i)
#define dF(i, a, b) for (int i = (a); i >= (b); --i)
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int N = 20, K = 1 << N;
int n, pcnt[K]; ull f[N + 1][K], g[N + 1][K];
void FMT(ull *f, int n, ull tp) {
F(i, 0, n - 1) F(j, 0, (1 << n) - 1)
if (j >> i & 1) f[j] += tp * f[j ^ 1 << i];
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
F(i, 1, (1 << n) - 1) pcnt[i] = pcnt[i ^ (i & -i)] + 1;
F(i, 0, (1 << n) - 1) cin >> f[pcnt[i]][i];
g[0][0] = 1;
F(u, 1, n) {
int cur = (1 << u - 1);
F(i, 1, u) FMT(f[i] + cur, u - 1, 1ull);
F(i, 0, u - 1) FMT(g[i], u - 1, 1ull);
F(i, 1, u) F(j, 0, u - i)
F(k, 0, (1 << u - 1) - 1)
g[i + j][k + cur] += f[i][k + cur] * g[j][k];
F(i, 0, u - 1) FMT(g[i], u - 1, -1ull);
F(i, 1, u) FMT(g[i] + cur, u - 1, -1ull);
}
F(i, 0, (1 << n) - 1) cout << g[pcnt[i]][i] << " ";
return 0;
}
:::
集合幂级数
比如几个经典的正式赛题,我不说是什么。