题解:P3445 [POI 2006] TAN-Dancing in Circles

需要观察性质的计数。

题意:问 \(n\) 个人划分成 \(k\) 个环,每个环长度至少为 \(l\) 的方案数,对 \(2005\) 取模。\(k\le n\le 10^9,2\le l\)

做法:

发现这个东西严格强于第一类斯特林数,所以肯定不能直接做,发现模数很小,考虑做法应该和模数有点关系。

首先先把 \(2005\) 拆成 \(5\times 401\),最后再 crt 合并一下即可。

考虑如果我确定了环长分别为 \(a_1<a_2\cdots <a_t\),并且分别有 \(x_1,x_2\cdots x_t\) 个,模数为 \(p\) 要求为质数,那么总的方案数应该是

\[\frac{n!}{\prod (a_i!)^{x_i}x_i!}\times \prod (a_i-1)!^{x_i}=\frac{n!}{\prod a_i^{x_i}x_i!} \]

那么对这个柿子分析, 考虑其什么时候模 \(p\)\(0\),我们会有以下结论:

  • \(a_i\le p\)

考虑左侧的柿子,前面那个部分应该是整数,因为本质上考虑组合意义,是确定了每个数会分在哪一个环中,并排除了环的顺序,所以是整数。而对于 \(\prod (a_i-1)!^{x_i}\) 来说,这个东西就不能是 \(p\) 的倍数,那么要求 \(a_i\le p\) 才行。

  • \(f(n,k)\) 代表把 \(n\) 个人分成 \(k\) 个环,每个环大小大于等于 \(l\) 的方案数,\(q=\frac{n}{p}\),那么有结论,\(f(n,k)=(-1)^qf(n\bmod p,k-q)\)

考虑先证明 \(n\ge p\) 时,\(a_t=p\),然后即可归纳证明有 \(q\)\(p\) 长环,然后再计算系数等于 \((-1)^q\) 即可,先证明前者。

\(\operatorname{count}_p(x)\) 代表 \(x\)\(p\) 的幂次,那么等价于我们要证明,\(\operatorname{count}_p(n!)>\operatorname{count}_p(\prod x_i!)\)

根据经典结论,\(\operatorname{count}_p(x_i!)\le \operatorname{count}_p((\sum x_i)!)=\operatorname{count}_p(k!)\)。而因为题目中保证了 \(l\ge 2\),所以对于答案不为 \(0\) 的情况就有 \(k\le \frac{n}{2}\),因此几句证明了上面那个结论。

那么考虑系数,我要从 \(n\) 个数里面选出来 \(p\times q\) 个去组成 \(q\) 个大小为 \(p\) 的环,那么方案数应该为 \(\binom{n}{p\times q}\times \frac{\binom{p\times q}{p,p,\cdots p}}{q!}\times (p-1)!^q\)。分别考虑三部分模 \(p\) 结果。第一部分直接用 Lucas 定理得到为 \(1\),第二部分对上面的组合数直接拆开,把 \(p\) 约掉,并且发现 \((p-1)!\equiv\prod\limits_{i=kp+1}^{(k+1)p-1}i\pmod p\),所以上面其实是 \(q!\),和下面约掉了。第三部分由威尔逊定理,得到为 \((-1)^q\)。证毕。

那么直接按照上面的柿子就可以把 \(n,k\) 压到 \(401\) 的范围内,直接 dp 即可,复杂度 \(O(400^2)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 1005;
int dp[maxn][maxn], v[maxn], n, k, l;
int cal(int n, int k, int l, int mod) {
	if(n < k * l || k < 0)
		return 0;
//	cout << n << " " << k << " " << l << endl;
	memset(dp, 0, sizeof(dp));
	for (int i = l; i <= n; i++) {
		v[i] = 1;
		for (int j = i - l + 1; j < i; j++)
			v[i] = v[i] * j % mod;	
	}
	dp[0][0] = 1;
	for (int i = 1; i <= k; i++) 
		for (int j = 1; j <= n; j++) {
			dp[i][j] = dp[i][j - 1] * (j - 1) % mod;
			if(j >= l)
				dp[i][j] = (dp[i][j] + dp[i - 1][j - l] * v[j]) % mod;
		}
	return dp[k][n];
}
int solve(int n, int k, int l, int mod) {
	if(n <= mod)
		return cal(n, k, l, mod);
	int q = n / mod;
	return cal(n % mod, k - q, l, mod) * (q & 1 ? mod - 1 : 1) % mod;
}
signed main() {
	cin >> n >> k >> l;
	int a = solve(n, k, l, 5), b = solve(n, k, l, 401);
	while(b % 5 != a)
		b += 401;
	cout << b << endl;
	return 0;
}
posted @ 2025-11-10 10:35  LUlululu1616  阅读(10)  评论(0)    收藏  举报