幻化成风

有一个长为 \(m\) 的序列 \(\{a_i\}\) 和一个 \(n\),其中 \(n!\) 可以用下列方式表示:

\[n!=\prod {b_i}^{a_i} \]

其中 \(\{b_i\}\) 中的数两两不同。对不同表达方式计数。\(\bmod 10^9+7\)\(1\le n\le 10^4,1\le m,\sum a\le 30\)

两种表示方式不同当且仅当集合 \(\{(b_i, a_i)\}\) 不同。现在你需要对不同表示方式计数。答案对 \(10^9+7\) 取模。

考虑题中的两个限制怎么办。\(\{b_i,a_i\}\) 本质不同在 \(b_i\) 互不相同的情况下其实就是对于相同的 \(a_i\) 满足 \(b_i\) 无序。那我们直接当成无序做,然后除去 \(\prod cnt(a_i)\) 即可。

考虑 \(b_i\) 互不相同的限制。这让这道题非常难以直接下手。这个时候考虑使用 集合不相等容斥,枚举全集的划分 \(\mathcal S\),满足 \(\mathcal S\) 内任意集合的所有元素全相等,那么答案就是 $\sum\limits_{\mathcal S}f(\mathcal S)\prod\limits_{s\in \mathcal S} (|s|-1)!\times (-1)^{|s|-1} $。其中 \(f(\mathcal S)\) 表示这种集合划分的对应答案。后面的是容斥系数。

直接枚举 \(\mathcal S\) 然后算当然相当寄,考虑我们计算 \(f(\mathcal S)\) 和后面的容斥系数需要什么:需要 \(\mathcal S\) 中所有元素 \(s\)\(\sum\limits_{u\in s} a_u\)\(|s|\)。而不需要关系 \(a\) 具体的划分情况。

这样的话,我们爆搜出来所有的本质不同的 \(\mathcal S\),发现在 \(a_i=1\) 的极限情况下等价于 \(\mathrm{bell}(\sum a)\),也即 \(\sum a\le 30\) 的拆分数个数。而我们有 \(\mathrm{bell}(\sum a)\le \mathrm{bell}(30)=5604\),记 \(T=5604\),现在对着每一种状态算原先的答案。

因为质因子互相独立,我们算出来 \(p_i\)\(n!\) 内出现的次数 \(lim_i\),那么就相当于一个完全背包,\(\mathcal O(nm)\) 做一遍即可。时间复杂度 \(\mathcal O(nmT)\)

#include <bits/stdc++.h>
#define pb emplace_back
#define fir first
#define sec second

using i64 = long long;
using u64 = unsigned long long;
using pii = std::pair<int, int>;

constexpr int maxn = 1e4 + 5, maxm = 30 + 5;
constexpr int mod = 1e9 + 7;
constexpr u64 base = 19260817;
void add(int& x, int y) { if ((x += y) >= mod) x -= mod; return ; }
void sub(int& x, int y) { if ((x -= y) < 0) x += mod; return ; }
int inc(int x, int y) { return (x + y) >= mod ? (x + y - mod) : (x + y); }
int dec(int x, int y) { return (x - y) < 0 ? (x - y + mod) : (x - y); }
int power(int x, int y) {
	int ans = 1;
	for (; y; y >>= 1, x = (i64)x * x % mod) if (y & 1) ans = (i64)ans * x % mod;
	return ans;
}
int fac[maxm], ifac[maxm], ans, a[maxm], n, m, prime[maxn], cnt, lim[maxn], f[maxn], buc[maxm];
bool flg[maxn];
u64 pw[maxm << 1];
int C(int n, int m) {
	if (n < 0 || m < 0 || n < m) return 0;
	return (i64)fac[n] * ifac[n - m] % mod * ifac[m] % mod;
}
void init(int n) {
	for (int i = fac[0] = 1; i <= n; ++i)
		fac[i] = (i64)fac[i - 1] * i % mod;
	ifac[n] = power(fac[n], mod - 2);
	for (int i = n - 1; ~i; --i)
		ifac[i] = (i64)ifac[i + 1] * (i + 1) % mod;	
	for (int i = pw[0] = 1; i <= n * 2; ++i)
		pw[i] = (u64)pw[i - 1] * base;
	return ;
}
void sieve(int n) {
	for (int i = 2; i <= n; ++i) {
		if (!flg[i]) prime[++cnt] = i;
		for (int j = 1; j <= cnt && i * prime[j] <= n; ++j) {
			flg[i * prime[j]] = true;
			if (i % prime[j] == 0) break ;
		}
	}
	for (int i = 1; i <= cnt; ++i) {
		int x = n;
		while (x) lim[i] += x / prime[i], x /= prime[i];
	}
	return ;
}
int sgn(int x) { return (x & 1) ? (mod - 1) : 1; }

struct info {
	pii a[maxm];
	u64 hash;
	info() { memset(a, 0, sizeof(a)); hash = 0; }
	void gethash() {
		std::sort(a, a + m, std::greater<pii>()); hash = 0;
		for (int i = 0; i < m && a[i].fir; ++i)
			hash += a[i].fir * pw[i] + a[i].sec * pw[i + 30];
		return ;
	}
	bool operator < (const info& rhs) const {
		return hash < rhs.hash;
	}
} tmp;
std::map<info, int> dp[2];
std::map<std::vector<int>, int> coef;

int main() {
	freopen("count.in", "r", stdin);
	freopen("count.out", "w", stdout);
	scanf("%d %d", &n, &m);
	init(30); sieve(n);
	for (int i = 1; i <= m; ++i)
		scanf("%d", &a[i]);
	std::sort(a + 1, a + 1 + m);
	int now = 0; dp[now][info()] = 1;
	for (int i = 1; i <= m; ++i) {
		now ^= 1; dp[now].clear();
		for (auto& [key, val] : dp[now ^ 1]) {
			for (int k = 0; k < m && (k == 0 || key.a[k - 1].fir > 0); ++k) {
				tmp = key; tmp.a[k].fir += a[i];
				++tmp.a[k].sec; tmp.gethash();
				add(dp[now][tmp], val);
			}
		}
	}
	for (auto& [key, val] : dp[now]) {
		std::vector<int> g; int w = val; 
		for (int k = 0; k < m && key.a[k].fir; ++k)
			g.pb(key.a[k].fir), w = (i64)w * fac[key.a[k].sec - 1] % mod * sgn(key.a[k].sec - 1) % mod;
		add(coef[g], w); g.clear();
	}
	for (auto& [key, val] : coef) {
		memset(f, 0, sizeof(f)); f[0] = 1;
		for (auto& v : key)
			for (int j = v; j <= n; ++j)
				add(f[j], f[j - v]);
		int res = val;
		for (int i = 1; i <= cnt; ++i)
			res = (i64)res * f[lim[i]] % mod;
		add(ans, res);
	}
	for (int i = 1; i <= m; ++i)
		++buc[a[i]];
	for (int i = 1; i <= 30; ++i)
		if (buc[i]) ans = (i64)ans * ifac[buc[i]] % mod;
	printf("%d\n", ans);
	return 0;
}
posted @ 2023-11-08 10:06  ImALAS  阅读(21)  评论(0编辑  收藏  举报