1 条题解

  • 0
    @ 2026-10-26 5:03:37

    思路与算法

    固定牡牛数为 bb。若 b=0b=0,只有全牝牛一种。若 b>0b>0,在相邻牡牛之间先安排必需的 KK 只牝牛,消耗 K(b−1)K(b-1) 个位置。压缩这些固定间隔后,剩余长度 L=N−K(b−1)L=N-K(b-1),只需从中选 bb 个位置放牡牛,方案数为 (Lb)\binom Lb。压缩与恢复间隔一一对应,未被选的位置放牝牛。

    因此答案为

    $$1+\sum_{b\ge1,\ N-K(b-1)\ge b}\binom{N-K(b-1)}b\pmod {5000011}.$$

    模数是素数,且阶乘上界 N≤105<5000011N\le10^5<5000011,可预处理阶乘、逆阶乘,常数时间求每个组合数。按 bb 递增累加,首次 L<bL<b 时终止。间隔压缩值用64位临时变量。

    正确性说明

    任一合法队列在相邻两牡牛间至少有 KK 只牝牛,删除每个间隔最先的这 KK 只后,得到长为 LL 的普通二元队列及其 bb 个牡牛位置。反之,对任意选出的 bb 个位置,在每对相邻牡牛之间补回 KK 只牝牛,得到且仅得到一个合法原队列。因此固定 bb 的队列数为 (Lb)\binom Lb。不同牡牛数的集合互不相交,连同无牡牛方案求和完整覆盖所有队列。

    当 L<bL<b 时无法容纳 bb 牡牛;以后 L−b=N+K−(K+1)bL-b=N+K-(K+1)b 继续减小,故终止安全。阶乘均非零模素数,逆阶乘公式合法,逐项取模保持总数余数,输出正确。

    复杂度

    预处理阶乘和逆阶乘 O(N)O(N),一次模逆快速幂 O(log⁡P)O(\log P),P=5000011P=5000011。枚举 B=⌊(N+K)/(K+1)⌋B=\lfloor(N+K)/(K+1)\rfloor 种正牡牛数,时间 O(B)O(B)。含初始化总时间 O(N+B+log⁡P)O(N+B+\log P),额外空间 O(N)O(N)。即使 KK 很大,阶乘初始化的 O(N)O(N) 仍需计入。

    易错点

    1. 两端不需要牝牛间隔,固定消耗是 K(b−1)K(b-1),不是 KbKb。
    2. 无牡牛方案须额外加1。K=0K=0 时结果为 2N2^N,K=N−1K=N-1 时最多一只牡牛。
    3. 公母牛各自相同,只选位置,不再乘 b!b!。
    4. 只在 0≤b≤L≤N0\le b\le L\le N 时访问阶乘数组;模数素且预处理上界小于模数是逆阶乘成立条件。
    5. 覆盖 N=1N=1、最大 NN、K=0,1,N−1K=0,1,N-1;独立答案使用末位符号DP,并以所有 N≤12N\le12 二元串枚举核对。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long;
    const int MOD = 5000011;
    ll power(ll a, ll e) {
        ll r = 1;
        while (e) {
            if (e & 1)
                r = r * a % MOD;
            a = a * a % MOD;
            e >>= 1;
        }
        return r;
    }
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, k;
        cin >> n >> k;
        vector<ll> fact(n + 1), inv(n + 1);
        fact[0] = 1;
        for (int i = 1; i <= n; i++)
            fact[i] = fact[i - 1] * i % MOD;
        inv[n] = power(fact[n], MOD - 2);
        for (int i = n; i; i--)
            inv[i - 1] = inv[i] * i % MOD;
        ll answer = 1;
        for (int bulls = 1; bulls <= n; bulls++) {
            ll spaces = n - 1LL * k * (bulls - 1);
            if (spaces < bulls)
                break;
            ll ways = fact[spaces] * inv[bulls] % MOD * inv[spaces - bulls] % MOD;
            answer = (answer + ways) % MOD;
        }
        cout << answer << '\n';
    }
    
    • 1

    信息

    ID
    1665
    时间
    1000ms
    内存
    512MiB
    难度
    普及
    标签
    递交数
    1
    已通过
    1
    上传者