251105B. 换来换去/card

251105B. 换来换去/card

计数将 \(n\) 个区分的物品划入任意个大小 \(\ge 2\) 的不区分集合的方案数。

\[n\le 10^7 \]


首先,这个问题看起来很像贝尔数:

计数将 \(n\) 个区分的物品划入任意个不区分集合的方案数。

这个描述中实际上隐含了集合大小 \(\ge 1\) 的条件。

考虑贝尔数的 EGF\(\exp(\exp(x)-1)\),这里内层的 \(-1\) 减去了集合为空的情况。

类似地,考虑回原问题,由于集合大小 \(\ge 2\)。我们减去集合大小为 \(0\)\(1\) 的情况。

所以答案的 EGF 应为 \(\exp(\exp(x)-x-1)\)

展开:

\[\begin{align*} &e^{e^x-x-1} \\=&e^{-x}e^{e^x-1} \\=&e^{-x}\sum_k\frac 1{k!}(e^x-1)^k \\=&e^{-x}\sum_k\frac 1{k!}\sum_{j=0}^k\binom kje^{jx}(-1)^{k-j} \\=&e^{-x}\sum_k\frac 1{k!}\sum_{j=0}^k\frac{k!}{i!j!}e^{jx}(-1)^i \\=&\sum_{i+j\le n}\frac 1{i!j!}e^{(j-1)x}(-1)^i \end{align*} \]

提出第 \(n\) 项系数:

\[\begin{align*} &[{x^n}/{n!}]e^{e^x-x-1} \\=&\sum_{i+j\le n}\frac 1{i!j!}([{x^n}/{n!}]e^{(j-1)x})(-1)^i \\=&\sum_{i+j\le n}\frac 1{i!j!}(j-1)^n(-1)^i \\=&\sum_{j=0}^n\frac {(j-1)^n}{j!}\sum_{i=0}^{n-j}\frac{(-1)^i}{i!} \end{align*} \]

后半部分是一个与 \(j\) 无关的前缀和。前半部分可以通过线性筛线性求。分别预处理即可。


同样的,我们也可以线性求单项贝尔数,按照类似的方法有:

\[ \begin{align*} &[x^n/n!]e^{e^x-1} \\=&\sum_{i+j\le n}\frac 1{i!j!}[x^n/n!]e^{jx}(-1)^i \\=&\sum_{j=0}^n\frac {j^n}{j!}\sum_{i=0}^{n-j}\frac{(-1)^i}{i!} \end{align*} \]


code
#include <iostream>
#include <algorithm>
#include <bitset>

const int N = 1e7 + 7;
typedef long long i64;

namespace # {
int n, MOD;

i64 fpow(i64 x, i64 k) {
	i64 res = 1;
	for(; k; k >>= 1, x = x * x %MOD)
		if(k & 1) res = res * x %MOD;
	return res;
}
#define inv(x) fpow((x), MOD-2)

std::bitset<N> ip;
std::basic_string<int> pr;
int pown[N];
void prime() {
	pr.clear();
	for(int i = 2; i <= n; ++i) {
		if(!ip[i]) pr += i, pown[i] = fpow(i, n);
		else ip[i] = 0;
		for(int& p: pr) {
			if(i * p > n) break;
			ip[i * p] = 1;
			pown[i * p] = 1ll * pown[i] * pown[p] %MOD;
			if(i % p == 0) break;
		}
	}
}

inline int getpow(int x) { return x == -1 ? n & 1 ? -1 : 1 : x <= 1 ? x : pown[x]; }
inline int gmod(i64 x) { return x - (x >= MOD) * MOD; }

int frac[N], ifac[N], pre[N];
inline void main() {
	std::cin >> n >> MOD;
	ifac[0] = frac[0] = 1;
	for(int i = 1; i <= n; ++i) {
		frac[i] = 1ll * frac[i-1] * i %MOD;
	}
	ifac[n] = inv(frac[n]);
	for(int i = n; i >= 1; --i) {
		ifac[i-1] = 1ll * ifac[i] * i %MOD;
	}
	pre[0] = 1;
	for(int i = 1; i <= n; ++i) {
		if(i & 1) pre[i] = gmod(pre[i-1] + MOD - ifac[i]);
		else pre[i] = gmod(pre[i-1] + ifac[i]);
	}
	prime();
	int ans = 0;
	for(int k = 0; k <= n; ++k) {
		int res = 1ll * ifac[k] * getpow(k-1) %MOD * pre[n-k] %MOD;
		ans = gmod(ans + res);
	}
	std::cout << ans << "\n";
}

};

int main() {
	freopen("card.in", "r", stdin), freopen("card.out", "w", stdout);
	std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
	int t; std::cin >> t;
	while(t--) #::main();
}
posted @ 2025-11-05 15:55  CuteNess  阅读(35)  评论(0)    收藏  举报