题解 : 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\) 升序):

\[\frac{n!}{\prod_{i=1}^m (a_i!)^{k_i} \times k_i!} \times \prod_{i=1}^m (a_i- 1)!^{k_i} \]

这个式子有以下性质:

  1. 当 \(a_m>p\) 时,\((a_m-1)!\equiv 0 \pmod p\), 所以我们可以认为 \(a_m \le p\)。
  2. 当 \(n \ge p\) 且模 \(p\) 非零时,考虑上面式子的另一种形式,令 \(K = \Sigma k_i\)

\[\frac{n!}{\prod a_i^{k_i} \times k_i!} \]

假设 \(a_m < p\),有

\[\begin{cases} \nu_p\left( \frac{n!}{\prod a_i^{k_i} k_i!} \right) = \nu_p(n!) - \nu_p\left( \prod k_i! \right)\\ \nu_p(\prod k_i!) \le \nu_p(K!) \\ \nu_p(n!) > \nu_p(K!) ,(K \le n/2) \end{cases} \]

于是 \(\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\) 证明:

\[f(n,k)= \frac{n!}{(p!)^q \times q! r!} \times [(p-1)!]^q \times f(r,k-q) \\ = \frac{n!}{p^q \times q! r!} \times f(r,k-q) \\ \]

因为

\[\frac{n!}{r!} \equiv r! \pmod p \]

所以

\[f(n,k) \equiv \frac{1}{p^q \times q!} \times f(r,k-q) \\ \]

由 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\)。

所以:

\[\frac{1}{p^q q!} \equiv (-1)^q \pmod{p}. \]

代入得:

\[f(n,k) \equiv (-1)^q f(r, k-q) \pmod{p}. \]

由于 \(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;
}
posted @ 2026-05-21 10:37  Koswel  阅读(13)  评论(0)    收藏  举报