题解:十二重计数法
前置:
- 计数基础(组合数,斯特林数)
- 多项式基础(多项式 exp)
题面:
有 \(n\) 个球和 \(m\) 个盒子,要全部装进盒子里。
还有一些限制条件,那么有多少种方法放球?(与放的先后顺序无关)
限制条件分别如下:
\(\text{I}\):球之间互不相同,盒子之间互不相同。
\(\text{II}\):球之间互不相同,盒子之间互不相同,每个盒子至多装一个球。
\(\text{III}\):球之间互不相同,盒子之间互不相同,每个盒子至少装一个球。
\(\text{IV}\):球之间互不相同,盒子全部相同。
\(\text{V}\):球之间互不相同,盒子全部相同,每个盒子至多装一个球。
\(\text{VI}\):球之间互不相同,盒子全部相同,每个盒子至少装一个球。
\(\text{VII}\):球全部相同,盒子之间互不相同。
\(\text{VIII}\):球全部相同,盒子之间互不相同,每个盒子至多装一个球。
\(\text{IX}\):球全部相同,盒子之间互不相同,每个盒子至少装一个球。
\(\text{X}\):球全部相同,盒子全部相同。
\(\text{XI}\):球全部相同,盒子全部相同,每个盒子至多装一个球。
\(\text{XII}\):球全部相同,盒子全部相同,每个盒子至少装一个球。
\(\text{I}\)
容易发现是 \(m^n\),每个球可以任意选。
\(\text{II}\)
枚举哪些不放球,\(C_m^n\)。
\(\text{III}\)
因为非空,那么每一种方案每个盒子间都是本质不同的,所以其实就是斯特林数乘阶乘,但是这里给出更为普遍一点的推法。
设 \(G_i\) 为钦定 \(i\) 个盒子可空方案数,\(F_i\) 为 \(i\) 个盒子不空方案数。
那么:
所以答案为:
\(\text{IV}\)
考虑枚举多少个非空,因为盒子本质不同,所以不需要知道哪些为空。
剩下的盒子见 \(\text{VI}\),所以答案就是 \(\sum_{i=0}^m{n \brace i}\)
\(\text{V}\)
发现交换两个盒子的所有球是相同的方案,所以其实只有一种合法方案。
答案是:\([n\le m]\)
\(\text{VI}\)
这个其实就是将 \(n\) 个本质不同的数划分为 \(m\) 个非空集合的方案数。
观察 \(\text{III}\) 得知,在那个方案数统计中,每一个划分方案都会被计算 \(m!\) 次,所以其实就得到了:
注意到 \(\text{IV}\) 需要求 \(n\brace i\),那么考虑生成函数。
设 \(F_n=\sum_{i=0}^{+\infty}{{n \brace i}x^i}\)。
不过其实就只有 \(n+1\) 项有值。
由刚刚的推导得知:
得到了一个卷积形式:
直接 NTT 即可。
\(\text{VII}\)
方案数只与每个盒子内的球数有关,直接插板法,先放 \(m\) 个球在每个里面,\(C_{n+m-1}^{m-1}\)。
\(\text{VIII}\)
枚举哪些盒子是空的,\(C_{m}^{n}\)。
\(\text{IX}\)
更简单的插板 \(C_{n-1}^{m-1}\)
\(\text{X}\)
假设方案数是 \(p_{n,m}\),那么可以发现其实是在统计有多少种单调不降的序列,所有数的和是 \(n\)。
这样一个序列一定是通过若干次前缀加构成的。
每次新加一个数考虑是否进行前缀加:
其实进行一些更为朴素的 \(dp\),也可以推得,不过需要推的比较复杂。
但是这是 \(O(n^2)\) 的。
怎么优化呢?
首先现将转移更改一下:
惊奇的发现如果设 \(P_{i}=\sum_{k=0}^{+\infty}{p_{k,i}x^k}\) 可以得到:
那么:
此时多项式求 \(\ln\) 再求 \(\exp\) 可得(\(\ln\) 只需手模泰勒展开或简单求导积分即可):
\(\text{XI}\)
还是 \([n\le m]\)。
\(\text{XII}\)
先将 \(m\) 个球放进去,答案是 \(p_{n-m,m}\)。
代码:
点击查看代码
点击查看代码
点击查看代码
点击查看代码
点击查看代码
点击查看代码
点击查看代码
点击查看代码
点击查看代码
点击查看代码
const int N = 8e5 + 10, mod = 998244353;#define int long long#define emp emplace_back
#define pb push_back
#define fi first
#define se secondusing pii = pair <int, int>;int fac[N], inv[N], s[N], p[N], f[N], g[N];int qpow(int x, int b)
{int res = 1;while (b){if (b & 1) res = res * x % mod;x = x * x % mod;b >>= 1;}return res;
}int rev[N];
void NTT(int *a, int k, bool op = 0)
{for (int i = 0; i < k; i++) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? k >> 1 : 0);for (int i = 0; i < k; i++) if (i < rev[i]) swap(a[i], a[rev[i]]);for (int len = 2; len <= k; len <<= 1){int wn = qpow(3, (mod - 1) / len);for (int l = 0, mid = (len >> 1) - 1; l + len - 1 < k; l += len, mid += len){int w = 1;for (int i = l; i <= mid; i++, w = w * wn % mod){int x = a[i], y = a[i + (len >> 1)] * w % mod;a[i] = x + y;if (a[i] >= mod) a[i] -= mod;a[i + (len >> 1)] = x - y;if (a[i + (len >> 1)] < 0) a[i + (len >> 1)] += mod;}}}if (op){reverse(a + 1, a + k);int inv = qpow(k, mod - 2);for (int i = 0; i < k; i++) a[i] = a[i] * inv % mod;}
}void fill(int *f, int l, int r, int v) {for (int i = l; i < r; i++) f[i] = v;}void copy(int *f, int *h, int l, int r) {for (int i = l; i < r; i++) h[i] = f[i];}int mulf[N], mulg[N];
void mul(int *f, int *g, int *h, int n, int m)
{int len = 1;while (len < n + m) len <<= 1;fill(mulf, 0, len, 0), fill(mulg, 0, len, 0);copy(f, mulf, 0, len), copy(g, mulg, 0, len);NTT(mulf, len, 0), NTT(mulg, len, 0);for (int i = 0; i < len; i++) h[i] = mulf[i] * mulg[i] % mod;NTT(h, len, 1);for (int i = n + m - 1; i < len; i++) h[i] = 0;
}int invh[N], invf[N];void Inv(int *f, int *h, int n)
{if (n == 1) return h[0] = qpow(f[0], mod - 2), void();Inv(f, h, (n + 1) >> 1);int len = 1;while (len < 2 * n) len <<= 1;fill(invf, 0, len, 0);copy(f, invf, 0, n);NTT(invf, len, 0), NTT(h, len, 0);for (int i = 0; i < len; i++) h[i] = h[i] * (2 - h[i] * invf[i] % mod + mod) % mod;NTT(h, len, 1);fill(h, n, len, 0);
}void dev(int *f, int len) {for (int i = 1; i < len; i++) f[i - 1] = i * f[i] % mod; f[len - 1] = 0;}
void redev(int *f, int len) {for (int i = len - 1; i >= 0; i--) f[i + 1] = f[i] * qpow(i + 1, mod - 2) % mod; f[0] = 0;}int lnf[N], lng[N];void ln(int *f, int *h, int n)
{fill(lnf, 0, 2 * n, 0), fill(lng, 0, 2 * n, 0);copy(f, lnf, 0, n);dev(lnf, n);Inv(f, lng, n);mul(lnf, lng, h, n, n);redev(h, n);fill(h, n, 2 * n, 0);
}int _expf[N], expg[N];void exp(int *f, int *h, int n)
{if (n == 1) return h[0] = 1, void();exp(f, h, (n + 1) >> 1);fill(_expf, 0, 2 * n, 0);fill(expg, 0, 2 * n, 0);copy(h, _expf, 0, n);fill(_expf, n, 2 * n, 0);ln(_expf, expg, n);for (int i = 0; i < n; i++) expg[i] = (-expg[i] + f[i] + mod) % mod;expg[0]++;mul(_expf, expg, h, n, n);fill(h, n, 2 * n, 0);
}// #pragma region countint C(int n, int m) {return n >= m ? fac[n] * inv[m] % mod * inv[n - m] % mod : 0;}int A(int n, int m) {return C(n, m) * fac[m] % mod;}int I(int n, int m) {return qpow(m, n);}int II(int n, int m) {return A(m, n);}int III(int n, int m)
{int flag = m & 1 ? -1 : 1, ans = 0;for (int i = 0; i <= m; i++) ans = (ans + flag * qpow(i, n) % mod * C(m, i) % mod + mod) % mod, flag = -flag;return ans;
}int IV(int n, int m)
{int ans = 0;for (int i = 0; i <= m; i++) ans = (ans + s[m - i]) % mod;return ans;
}int V(int n, int m) {return n <= m;}int VI(int n, int m) {return s[m];}int VII(int n, int m) {return C(n + m - 1, m - 1);}int VIII(int n, int m) {return C(m, n);}int IX(int n, int m) {return C(n - 1, m - 1);}int X(int n, int m) {return p[n];}int XI(int n, int m) {return n <= m;}int XII(int n, int m) {return p[n - m];}// #pragma endregionsigned main()
{// freopen("data.in", "r", stdin); freopen("data.out", "w", stdout);// freopen("mission.in", "r", stdin); freopen("mission.out", "w", stdout);ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);int n, m; cin >> n >> m;fac[0] = inv[0] = 1;for (int i = 1; i <= n + m; i++) fac[i] = fac[i - 1] * i % mod, inv[i] = qpow(fac[i], mod - 2);for (int i = 0; i <= n; i++) f[i] = qpow(i, n) * inv[i] % mod;for (int i = 0; i <= n; i++) g[i] = (i & 1 ? mod - 1 : 1) * inv[i] % mod;mul(f, g, s, n + 1, n + 1);fill(s, n + 1, N, 0);fill(f, 0, N, 0);for (int i = 1; i <= m; i++){for (int j = 1; i * j <= n; j++){(f[i * j] += qpow(j, mod - 2)) %= mod;}}exp(f, p, n + 1);cout << I(n, m) << '\n';cout << II(n, m) << '\n';cout << III(n, m) << '\n';cout << IV(n, m) << '\n';cout << V(n, m) << '\n';cout << VI(n, m) << '\n';cout << VII(n, m) << '\n';cout << VIII(n, m) << '\n';cout << IX(n, m) << '\n';cout << X(n, m) << '\n';cout << XI(n, m) << '\n';cout << XII(n, m) << '\n';return 0;
}