题解 : P3445 [POI 2006] TAN-Dancing in Circles
神仙题。
问题是有 \(n\) 个不同的元素,把它划分成 \(k\) 个环,要求每个环大小至少为 \(L\),求方案数对 \(2005\) 取模。
最暴力的就是设状态为 \(i\) 个人组成 \(j\) 个环的方案数,然后 \(O(1)\) 转移。考虑优化,显然在状态上做文章是没有前途的,但观察到模数很小,猜到有些情况是会被模为 \(0\) 的。把 \(n\) 个人分成 \(k_1\) 个大小为 \(a_1\) 的环,\(k_2\) 个大小为 \(a_2\) 的环,… \(k_m\) 个大小为 \(a_m\) 的环的方案 (不妨令 \(k\) 升序):
这个式子有以下性质:
- 当 \(a_m>p\) 时,\((a_m-1)!\equiv 0 \pmod p\), 所以我们可以认为 \(a_m \le p\)。
- 当 \(n \ge p\) 且模 \(p\) 非零时,考虑上面式子的另一种形式,令 \(K = \Sigma k_i\)
假设 \(a_m < p\),有
于是 \(\nu_p(n!) > \nu_p(\prod k_i!)\),原式 \(\equiv 0 \pmod p\),矛盾。
因此 \(a_m = p\)。
由性质二,我们每次让 \(a\) 的最后一个元素为 \(p\),递推得到一定会分成最多 \(q\) 个大小为 \(p\) 的环,即 \(q = \left\lfloor \frac{n}{q} \right\rfloor,n=qp+r\)。
先说结论,\(f(n,k)=(-1)^qf(r,k-q) \pmod p\) 证明:
因为
所以
由 Lucas 定理,\(\binom{pq}{p,p,\dots,p} = \frac{(pq)!}{(p!)^q q!} \equiv 1 \pmod{p}\),而 \(\frac{(pq)!}{p^q q!} = \binom{pq}{p,p,\dots,p} \cdot [(p-1)!]^q \equiv 1 \cdot (-1)^q\)。
所以:
代入得:
由于 \(2005\) 不是质数,我们把 \(2005\) 分成 \(5\) 和 \(401\),做两次 dp,最后暴力枚举答案即可合并,代码很短
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i, l, r) for(int i = (l); i <= (r); ++ i)
#define per(i, r, l) for(int i = (r); i >= (l); -- i)
#define fi first
#define se second
#define endl '\n'
#define pii pair<int, int>
#define pb push_back
using namespace std;
const int N = 1e6 + 10;
int dp[405][405], fac[405], inv[405];
int A(int x, int y, int p) {
return fac[x] * inv[x - y] % p;
}
int qpow(int a, int b, int p) {
int res = 1;
for (; b; b >>= 1, a = a * a % p)
if (b & 1)
res = res * a % p;
return res;
}
int DP(int n, int k, int l, int p) {
if(n < k * l || k < 0) return 0;
k = min(k, n / l);
memset(dp, 0, sizeof dp);
inv[0] = fac[0] = 1;
rep(i, 1, n) fac[i] = fac[i - 1] * i % p;
inv[n] = qpow(fac[n], p - 2, p);
per(i, n - 1, 1) inv[i] = inv[i + 1] * (i + 1) % p;
dp[0][0] = 1;
rep(i, 1, n) {
rep(j, 0, k) {
dp[i][j] = dp[i - 1][j] * (i - 1) % p;
if(i >= l && j)
dp[i][j] = (dp[i][j] + dp[i - l][j - 1] * A(i - 1, l - 1, p) % p) % p;
}
}
return dp[n][k];
}
void Jail() {
int n, k, l;
cin >> n >> k >> l;
int q1 = n / 401, f1 = DP(n % 401, k - q1, l, 401);
if(q1 & 1) f1 = (401 - f1) % 401;
int q2 = n / 5, f2 = DP(n % 5, k - q2, l, 5);
if(q2 & 1) f2 = (5 - f2) % 5;
rep(i, 0, 2004) {
if(i % 401 == f1 && i % 5 == f2) {
cout << i << endl;
return ;
}
}
}
signed main() {
// freopen("a.in", "r", stdin);
// freopen("a.out", "w", stdout);
ios :: sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int T = 1;
// cin >> T;
while(T -- ) Jail();
return 0;
}

浙公网安备 33010602011771号