1 条题解

  • 0
    @ 2026-10-26 17:31:11

    思路与算法

    每次离家直到首次回家称一个段。长度2j的段必须全部在同一侧,去掉首末步后与合法括号串一一对应,有2C(j-1)种。令N=n/2,M=m/2,得到f[N]=Σ(1≤j≤M)2C(j-1)f[N-j],f[0]=1。把x^N在特征多项式x^M-Σa[j]x^(M-j)下快速幂取余,再与前M项点积,求出大下标。

    正确性说明

    每条最终回家的散步按首次回家时刻唯一分段,每段长度≤m是原离家时间限制。首返段去边界后为正侧或负侧Dyck路径,给Catalan计数,乘法与分类求和得卷积递推。特征关系把x^M替换成低次项,恰与序列递推同构,故多项式快速幂的线性组合等于目标项。

    复杂度

    Catalan和初值初始化O(M²+MlogMOD),多项式乘法及约简O(M²),快速幂O(M²logN),空间O(M),包含临时卷积2M-1数组。M≤50。

    易错点

    n,m均为偶数。M=1时x模特征关系是常数2,程序单独设基。不要把任意回家段误当首返段,否则重复计数。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    const long long MOD = 1000000007;
    long long pw(long long a, long long n) {
        long long r = 1;
        while (n) {
            if (n & 1)
                r = r * a % MOD;
            a = a * a % MOD;
            n >>= 1;
        }
        return r;
    }
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        long long n;
        int m;
        cin >> n >> m;
        n /= 2;
        m /= 2;
        vector<long long> a(m + 1), cat(m), init(m);
        cat[0] = 1;
        for (int i = 1; i < m; i++)
            cat[i] = cat[i - 1] * (4 * i - 2) % MOD * pw(i + 1, MOD - 2) % MOD;
        for (int i = 1; i <= m; i++)
            a[i] = 2 * cat[i - 1] % MOD;
        init[0] = 1;
        for (int i = 1; i < m; i++)
            for (int j = 1; j <= i; j++)
                init[i] = (init[i] + a[j] * init[i - j]) % MOD;
        auto mul = [&](vector<long long> x, vector<long long> y) {
            vector<long long> z(2 * m - 1);
            for (int i = 0; i < m; i++)
                for (int j = 0; j < m; j++)
                    z[i + j] = (z[i + j] + x[i] * y[j]) % MOD;
            for (int i = 2 * m - 2; i >= m; i--)
                for (int j = 1; j <= m; j++)
                    z[i - j] = (z[i - j] + z[i] * a[j]) % MOD;
            z.resize(m);
            return z;
        };
        vector<long long> r(m), b(m);
        r[0] = 1;
        if (m == 1)
            b[0] = a[1];
        else
            b[1] = 1;
        while (n) {
            if (n & 1)
                r = mul(r, b);
            b = mul(b, b);
            n >>= 1;
        }
        long long ans = 0;
        for (int i = 0; i < m; i++)
            ans = (ans + r[i] * init[i]) % MOD;
        cout << ans << '\n';
    }
    
    • 1

    信息

    ID
    1818
    时间
    1000ms
    内存
    256MiB
    难度
    提高
    标签
    递交数
    1
    已通过
    1
    上传者