标签: 计数

  • CFGYM103415A CCPC2021广州A Math Ball

    题意: 有$n$种球,每种有无限个,同时第$i$种球有一个代价$c_i$,你要拿不超过$w$个球。如果最后第$i$种球你拿了$k_i$个,那么你会获得$\prod_{1\leq i\leq n}{k_i^{c_i}}$的权值,求所有合法方案的权值和。$n\leq 1e5,\sum{c_i}\leq 1e5,w\leq 10^{18}$

    $$  \text{考虑对于价值是}c_i\text{的球,构造生成函数}  \\  F_{c_i}\left( x \right) =\sum_{n\ge 0}{n^{c_i}x^n}  \\  \text{这样}\frac{\prod_i{F_{c_i}\left( x \right)}}{1-x}\text{的}w\text{次项即为答案}  \\  \text{设}F_k\left( x \right) =\sum_{n\ge 0}{n^kx^n},\text{显然可得}F_k\left( x \right) =x\frac{\mathrm{d}F_{k-1}\left( x \right)}{\mathrm{d}x}  \\  \text{进一步递推可得}, F_k\left( x \right) =\frac{\sum_{0\le i\le k}{T\left( k,i \right) x^i}}{\left( 1-x \right) ^{k+1}},\text{其中}T\left( k,i \right) \text{表示欧拉数}  \\  \text{考虑如何快速计算欧拉数}  \\  \text{首先由具体数学可得}  \\  \sum_{0\le i\le k}{T\left( k,i \right) \times \left( z+1 \right) ^i}=\sum_{0\le i\le k}{S_2\left( k,i \right) \times i!\times z^{k-i}}  \\  \text{进一步推导得欧拉数的通项公式}T\left( n,k \right) =\sum_{0\le i\le k}{\begin{array}{c}    \binom{n+1}{i}\left( -1 \right) ^i\left( k+1-i \right) ^n\\  \end{array}}  \\  \text{构造卷积形式}T\left( n,k \right) =\left( n+1 \right) !\sum_{0\le i\le k}{\frac{\left( -1 \right) ^i}{i!\left( n+1-i \right) !}}\times \left( k+1-i \right) ^n  \\  \text{卷积即可求出一行欧拉数。}  \\  \text{现在我们可以对每一个}c_i\text{计算出}F_{c_i}\left( x \right) \text{的分子,并且得到他们分子的乘积,设为}F\left( x \right)   \\  \text{设他们分母的乘积,再乘个}1-x\text{为}\left( 1-x \right) ^k  \\  \text{则答案即为}\frac{F\left( x \right)}{\left( 1-x \right) ^k}\text{的}w\text{次项系数}  \\  \text{即}\sum_{0\le i\le n}{\left[ x^i \right] F\left( x \right) \binom{w-i+k-1}{w-i}}\left( \text{广义二项式定理展开分母} \right)   \\  \text{令}a_i=\left[ x^i \right] F\left( x \right)   \\  \text{即}\sum_{0\le i\le n}{a_i\binom{w-i+k-1}{k-1}}  \\  \text{后者可以通过递推快速转移。}  \\    \\    \\  \frac{x^a}{\left( 1-x \right) ^b}=x^a\left( \sum_{n\ge 0}{\binom{-b}{n}\left( -1 \right) ^n}x^n \right)   \\  =x^a\left( \sum_{n\ge 0}{\frac{\left( -b \right) ^{\underline{n}}}{n!}\left( -1 \right) ^n}x^n \right)   \\  =x^a\left( \sum_{n\ge 0}{\frac{\left( -b-0 \right) \left( -b-1 \right) \left( -b-2 \right) ..\left( -b-\left( n-1 \right) \right)}{n!}\left( -1 \right) ^n}x^n \right)   \\  =x^a\left( \sum_{n\ge 0}{\frac{\left( b+0 \right) \left( b+1 \right) \left( b+2 \right) ..\left( b+\left( n-1 \right) \right)}{n!}}x^n \right)   \\  =x^a\left( \sum_{n\ge 0}{\frac{\left( b+n-1 \right) ^{\underline{n}}}{n!}}x^n \right)   \\  =\sum_{n\ge 0}{\frac{\left( b+n-1 \right) ^{\underline{n}}}{n!}x^{n+a}}  \\  =\sum_{n\ge 0}{\binom{b+n-1}{n}x^{n+a}}  \\  =\sum_{n\ge a}{\binom{b+n-a-1}{n-a}x^n}  \\  =\sum_{n\ge a}{\binom{b+n-a-1}{b-1}x^n}  \\  =\sum_{n\ge a}{\begin{array}{c}     \frac{\left( b-a+n-1 \right) ^{\underline{b-1}}}{\left( b-1 \right) !}x^n\\  \end{array}}  \\  \text{令}t=b-1  \\  \text{则}  \\  \sum_{n\ge a}{\begin{array}{c}      \frac{\left( t-a+n \right) ^{\underline{t}}}{t!}x^n\\  \end{array}}  \\  \text{从}a\text{推进到}a+1  \\  \frac{\left( t-a+n \right) ^{\underline{t}}}{\left( t-a+n-1 \right) ^{\underline{t}}}=\frac{t-a+n}{n-a}  \\    \\  10*9*8/\left( 9*8*7 \right) =10/7  \\    \\  \left( \frac{x^a}{\left( 1-x \right) ^b}\text{的}n\text{次项} \right)   \\    \\  \binom{n+b-1}{n}=\binom{n+b-1}{b-1}  \\  \binom{n+b-1}{n}\rightarrow \binom{n+b}{n+1}  \\    \\  \frac{\binom{n+b}{n+1}}{\binom{n+b-1}{n}}=\frac{n+b}{\left( n+1 \right)}  \\    \\  \frac{\left( n-2+b-i \right) !\left( b-1 \right) !\left( n-i \right) !}{\left( b-1 \right) !\left( n-i-1 \right) !\left( n-i+b-1 \right) !}  \\    \\  \frac{\left( n-i+b-2 \right) !\left( n-i \right)}{\left( n-i+b-1 \right) !}  \\  \frac{\left( n-i \right)}{\left( n-i+b-1 \right)}  \\    \\    $$
    #include <cinttypes>
    #include <cstring>
    #include <fstream>
    #include <iostream>
    #include <random>
    using int_t = int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 2e6;
    
    using i64 = int64_t;
    #ifdef NTTCNT
    std::ofstream nttcnt("nttcnt.txt");
    #endif
    const int_t mod = 998244353;
    const int_t g = 3;
    
    int_t power(int_t b, int_t i) {
        int_t r = 1;
        if (i < 0)
            i = ((i64)i % (mod - 1) + mod - 1) % (mod - 1);
        while (i) {
            if (i & 1)
                r = (i64)r * b % mod;
            b = (i64)b * b % mod;
            i >>= 1;
        }
        return r;
    }
    
    void makeflip(int_t* arr, int_t size2) {
        int_t len = (1 << size2);
        arr[0] = 0;
        for (int_t i = 1; i < len; i++) {
            arr[i] = (arr[i >> 1] >> 1) | ((i & 1) << (size2 - 1));
        }
    }
    
    int_t upper2n(int_t x) {
        int_t r = 0;
        while ((1 << r) < x)
            r++;
        return r;
    }
    template <int_t arg = 1>
    void transform(int_t* A, int_t size2, int_t* flip) {
        // #define int_t i64
        int_t len = (1 << size2);
    #ifdef NTTCNT
        nttcnt << len << endl;
    #endif
        for (int_t i = 0; i < len; i++) {
            int_t r = flip[i];
            if (r > i)
                std::swap(A[i], A[r]);
        }
        for (int_t i = 2; i <= len; i *= 2) {
            int_t mr = power(g, (i64)arg * (mod - 1) / i);
            for (int_t j = 0; j < len; j += i) {
                int_t curr = 1;
                for (int_t k = 0; k < i / 2; k++) {
                    int_t u = A[j + k], v = (i64)curr * A[j + k + i / 2] % mod;
                    A[j + k] = ((i64)u + v) % mod;
                    A[j + k + i / 2] = ((i64)u - v + mod) % mod;
                    curr = (i64)curr * mr % mod;
                }
            }
        }
        // #undef int_t
    }
    void poly_mul(const int_t* A, int_t n, const int_t* B, int_t m, int_t* C) {
        /*
           计算n次多项式A与m次多项式B的乘积
        */
        int_t size2 = upper2n(n + m + 1);
        int_t len = 1 << size2;
        static int_t T1[LARGE], T2[LARGE];
        for (int_t i = 0; i < len; i++) {
            if (i <= n)
                T1[i] = A[i];
            else
                T1[i] = 0;
            if (i <= m)
                T2[i] = B[i];
            else
                T2[i] = 0;
        }
        static int_t fliparr[LARGE];
        makeflip(fliparr, size2);
        transform(T1, size2, fliparr);
        transform(T2, size2, fliparr);
        for (int_t i = 0; i < len; i++)
            T1[i] = (i64)T1[i] * T2[i] % mod;
        transform<-1>(T1, size2, fliparr);
        int_t inv = power(len, -1);
        for (int_t i = 0; i <= n + m; i++)
            C[i] = (i64)T1[i] * inv % mod;
    }
    
    const int_t FLARGE = 2e5 + 10;
    
    int_t fact[FLARGE + 1], inv[FLARGE + 1], factInv[FLARGE + 1];
    void euler_num(int_t n, int_t* out) {
        static int_t P1[LARGE];
        static int_t P2[LARGE];
        for (int_t i = 0; i <= n; i++) {
            P1[i] = (i64)((i % 2) ? (mod - 1) : 1) * factInv[i] % mod *
                    factInv[n + 1 - i] % mod;
            P2[i] = power(i, n);
        }
        poly_mul(P1, n, P2, n, out);
        for (int_t i = 0; i <= n; i++)
            out[i] = (i64)out[i] * fact[n + 1] % mod;
    }
    
    using poly_t = std::vector<int_t>;
    poly_t poly_dcmul(const poly_t* P, int_t left, int_t right) {
        if (left == right)
            return P[left];
        int_t mid = (left + right) / 2;
        auto lret = poly_dcmul(P, left, mid);
        auto rret = poly_dcmul(P, mid + 1, right);
        poly_t ret;
        ret.resize(lret.size() - 1 + rret.size() - 1 + 1);
        poly_mul(&lret[0], lret.size() - 1, &rret[0], rret.size() - 1, &ret[0]);
        while (!ret.empty() && ret.back() == 0)
            ret.pop_back();
        return ret;
    }
    int_t C(int_t n, int_t m) {
        return (i64)fact[n] * factInv[m] % mod * factInv[n - m] % mod;
    }
    i64 getC(i64 n, int_t m) {
        if (n < 0 || n < m)
            return 0;
        i64 prod = 1;
        for (int_t i = 0; i < m; i++)
            prod = prod * (n % mod - i + mod) % mod;
        return prod * factInv[m] % mod;
    }
    int main() {
        std::ios::sync_with_stdio(false);
        cin.tie(0), cout.tie(0);
        {
            fact[0] = fact[1] = inv[1] = factInv[0] = factInv[1] = 1;
            for (int_t i = 2; i <= FLARGE; i++) {
                fact[i] = (i64)fact[i - 1] * i % mod;
                inv[i] = (i64)(mod - mod / i) * inv[mod % i] % mod;
                factInv[i] = (i64)factInv[i - 1] * inv[i] % mod;
            }
        }
        int_t n;
        i64 w;
        cin >> n >> w;
    
        static poly_t up[int_t(1e5 + 10)];
        static poly_t cache[int_t(1e5 + 10)];
        int_t sum = 0;
        for (int_t i = 1; i <= n; i++) {
            int_t c;
            cin >> c;
            sum += c + 1;
            poly_t& curr = up[i];
            if (!cache[c].empty()) {
                curr = cache[c];
                continue;
            }
            curr.resize(2 * c + 3);
            euler_num(c, &curr[0]);
            curr.resize(c + 1);
            cache[c] = curr;
    #ifdef DEBUG
            cout << "up " << i << " = ";
            for (auto t : curr)
                cout << t << " ";
            cout << endl;
    #endif
        }
        sum++;
        // for (int_t i = 0; i <= sum; i++) {
        //     down[i] = (i64)((i % 2) ? (mod - 1) : 1) * C(sum, i) % mod;
        // }
        auto upprod = poly_dcmul(up, 1, n);
        // cout << upprod.size() << endl;
        upprod.resize(sum + 1);
    #ifdef DEBUG
        cout << "up prod = ";
        for (auto t : upprod)
            cout << t << " ";
        cout << endl;
        cout << "sum = " << sum << endl;
    #endif
        // cout << "down size = " << sum << endl;
        // w -= cutcount;
        // if (w < 0) {
        //     cout << 0 << endl;
        //     return 0;
        // }
        // int_t ret = poly_divat(&upprod[0], down, upprod.size() - 1, sum, w);
        i64 ret = 0;
        {
            i64 b = sum;
            i64 n = w;
            i64 curr = getC(n - 0 + b - 1, b - 1);
    // C(n-i+b-1,b-1)到C(n-(i+1)+b-1,b-1)
    // getC(n - i + b - 1, b - 1)
    #ifdef DEBUG
            cout << "init curr = " << curr << endl;
    #endif
            for (i64 i = 0; i < upprod.size(); i++) {
                ret = (ret + upprod[i] * curr % mod) % mod;
                curr = curr * (n % mod - i + mod) % mod *
                       power((n % mod - i + b - 1 + (i64)3 * mod) % mod, -1) % mod;
                if (curr == 0 || n <= i - n + 1)
                    break;
    #ifdef DEBUG
                cout << "curr = " << curr << " at i = " << i << " with n = " << n
                     << endl;
    #endif
            }
            // int_t curr = getC(w - b + b - 1, b - 1);
            // for (i64 i = std::max<i64>(0, w - sum); i < upprod.size(); i++) {
            //     ret = (ret + curr * upprod[i] % mod) % mod;
            //     curr = curr * (n % mod + n % mod - sum % mod + mod + i) % mod *
            //            power(n + 1, -1) % mod;
            // }
            // i64 n = w;
            // i64 t = sum - 1;
            // i64 prod = 1;
            // // i64 curr = (t + n) % mod;
            // for (int_t i = 0; i < t; i++)
            //     prod = (prod * (t + n % mod - i) % mod);
            // for (int_t a = 0; a < upprod.size(); a++) {
            //     if (n >= a) {
            //         ret = (ret + prod * factInv[t] % mod * upprod[a] % mod) %
            //         mod;
            //     }
            //     prod = prod * (t - a + n % mod) % mod *
            //            power((n % mod - a + mod) % mod, -1) % mod;
            // }
        }
        cout << ret << endl;
        return 0;
    }

     

  • 生成函数计数笔记

    $$ \text{无标号无向连通图计数} \\ \text{设一般无向图的}EGF\,\,F\left( x \right) =\sum_{i\ge 0}{2^{\left( \begin{array}{c} i\\ 2\\ \end{array} \right)}\frac{x^i}{i!}} \\ \text{设不带重边自环的无向连通图的}EGF\text{为 }G\left( x \right) \\ \text{考虑任意一个无向图,一定是由0,1,2,3…..个不带重边自环的无向连通图组合而来的} \\ \text{故}F\left( x \right) =\text{e}^{G\left( x \right)}=\sum_{i\ge 0}{G\left( x \right) ^i\frac{x^i}{i!}} \\ \text{故}G\left( x \right) \equiv \log\text{ }F\left( x \right) \\ \\ \text{树:} \\ n\text{个点的带标号有根树有}n^{n-1}\text{个} \\ \text{故其}EGF\,\,F\left( x \right) =\sum_{i\ge 1}{i^{i-1}\frac{x^i}{i!}} \\ \text{基环树:} \\ \text{基环树是一个至少三个点组成的环加上若干个树} \\ F\left( x \right) =\frac{1}{2}\sum_{i\ge 3}{\left( i-1 \right) !\frac{F\left( x \right) ^i}{i!}} \\ \left( i-1 \right) !\text{是}i\text{个点的带标号环的个数} \\ F\left( x \right) ^i\text{是在环上的}i\text{个点分别连上树的方案。} \\ F\left( x \right) =\frac{1}{2}\sum_{i\ge 3}{\frac{F\left( x \right) ^i}{i}} \\ \text{因为}\ln \left( 1-x \right) =-\sum_{i\ge 1}{\frac{x^i}{i}} \\ \text{故}F\left( x \right) =-\frac{1}{2}\ln \left( 1-F\left( x \right) \right) +\frac{F\left( x \right)}{2}+\frac{F\left( x \right) ^2}{4}\left( \text{论文里后两项为负,我不确定正确性} \right) \\ $$

  • NOI2009 管道取珠

    $$ \text{如果给每个球标号,则出栈序列共有}\left( \begin{array}{c} n+m\\ n\\ \end{array} \right) \text{种。} \\ \left( \text{将第一个序列中的数安排到出栈序列中} \right) \\ \text{现在小球没有标号,只有两种颜色。} \\ \text{设}N\text{为本质不同的出栈序列数量,}a_i\text{为第}N\text{种出栈序列对应的操作序列个数} \\ \left( \text{每个操作序列对应着一种带标号的出栈序列} \right) \\ \text{显然}\sum_{1\le i\le N}{a_i}=\left( \begin{array}{c} n+m\\ n\\ \end{array} \right) \\ \text{对于}\sum_{1\le i\le N}{a_i^2} \\ \text{考虑样例,出栈序列为}BBA\text{的有两种方案,将这两种方案分别用}a\text{和}b\text{表示} \\ \text{则}a_i^2=\left( a+b \right) ^2=\left( a+b \right) \left( a+b \right) =a^2+2ab+b^2 \\ \text{可以理解为有两个人同时在两个不同的管道系统内取珠子,两个人都取到了第}i\text{种出栈序列的方案数。} \\ \text{设}f\left( u_1,d_1,u_2,d_2 \right) \text{表示第一个人的上管道取了}u_1\text{个,下管道取了}d_1\text{个,第二个人的上管道取了}u_2\text{个,下管道取了}d_2\text{时候,} \\ \text{满足出栈序列相同的操作序列的方案数。} \\ \text{设}U_x\text{为上方第}x\text{个珠子,}D_x\text{为下方第}x\text{个珠子} \\ f\left( u_1+\text{1,}d_1u_2+\text{1,}d_2 \right) +=\left[ U_{u_1+1}=U_{u_2+1} \right] f\left( u_1,d_1,u_2,d_2 \right) \\ f\left( u_1+\text{1,}d_1u_2,d_2+1 \right) +=\left[ U_{u_1+1}=D_{d_2+1} \right] f\left( u_1,d_1,u_2,d_2 \right) \\ f\left( u_1,d_1+\text{1,}u_2,d_2+1 \right) +=\left[ D_{d_1+1}=D_{d_2+1} \right] f\left( u_1,d_1,u_2,d_2 \right) \\ f\left( u_1,d_1+\text{1,}u_2+\text{1,}d_2 \right) +=\left[ D_{d_1+1}=U_{u_2+1} \right] f\left( u_1,d_1,u_2,d_2 \right) \\ \text{又因为任何时候都满足}u_1+d_1=u_2+d_2,\text{所以状态可以压缩。} \\ \text{滚动数组优化空间。} \\ \\ $$

    #include <cstring>
    #include <iostream>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t mod = 1024523;
    const int_t LARGE = 600;
    
    int_t dp[2][LARGE + 1][LARGE + 1];
    
    int_t U[LARGE + 1];
    int_t D[LARGE + 1];
    int_t n, m;
    int main() {
        cin >> n >> m;
        static char buf[LARGE + 10];
        scanf("%s", buf + 1);
        for (int_t i = 1; i <= n; i++) {
            U[n - i + 1] = buf[i] == 'A';
        }
        scanf("%s", buf + 1);
        for (int_t i = 1; i <= m; i++) {
            D[m - i + 1] = buf[i] == 'A';
        }
        dp[0][0][0] = 1;
        for (int_t i = 0; i <= n; i++) {
            auto& curr = dp[i & 1];
            auto& next = dp[(i ^ 1) & 1];
            memset(next, 0, sizeof(next));
            for (int_t j = 0; j <= m; j++) {
                for (int_t k = 0; k <= n; k++) {
                    const auto addTo = [](int_t& k, int_t val) {
                        k = (k + val + mod) % mod;
                    };
                    int_t l = i + j - k;
                    if (l < 0 || l > m) continue;
                    // A上(i),B上(k)
                    if (U[i + 1] == U[k + 1]) {
                        addTo(next[j][k + 1], curr[j][k]);
                    }
                    // A下(j),B下(l)
                    if (D[j + 1] == D[l + 1]) {
                        addTo(curr[j + 1][k], curr[j][k]);
                    }
                    // A上(i),B下(l)
                    if (U[i + 1] == D[l + 1]) {
                        addTo(next[j][k], curr[j][k]);
                    }
                    // A下(j),B上(k)
                    if (D[j + 1] == U[k + 1]) {
                        addTo(curr[j + 1][k + 1], curr[j][k]);
                    }
                }
            }
        }
        cout << dp[n & 1][m][n] << endl;
        return 0;
    }

     

  • $$ \text{题意:给定一个括号序列,求插入若干个括号,将这个括号序列变成} \\ \text{长度为}2\times n\text{的括号序列的方案数} \\ \\ \text{一开始考虑阶段设置为当前要在原串第几个字符后面加字符,然后发现根本没法转移}.. \\ \text{然后考虑把阶段设置为,当前放置了几个字符} \\ \text{设}f\left( n,m,k \right) \text{表示当前已经放置完成了}n\text{个字符,使用掉了原串的前}m\text{个字符} \\ \text{已经放置好了的字符串中,左括号比右括号多}k\text{个} \\ \text{时候的方案数} \\ \text{首先是边界条件}f\left( \text{0,0,}0 \right) =1 \\ \text{因为空串只有一种方案} \\ \text{转移时,首先考虑当前位置是左括号还是右括号。} \\ \text{然后如果这时候可以使用原串中的字符来完成转移,那么要优先使用原串字符} \\ \text{除非原串的字符不能完成这次转移,再去考虑加入新的字符} \\ \text{左括号时:} \\ f\left( n,m,k \right) =f\left( n,m,k \right) +f\left( n-\text{1,}m-\text{1,}k-1 \right) \left( \text{原串第}m\text{位置为左括号} \right) \\ f\left( n,m,k \right) =f\left( n,m,k \right) +f\left( n-\text{1,}m,k-1 \right) \left( \text{原串第}m\text{位置不是左括号} \right) \\ \text{右括号时:} \\ f\left( n,m,k \right) =f\left( n,m,k \right) +f\left( n-\text{1,}m-\text{1,}k+1 \right) \left( \text{原串第}m\text{位置为右括号} \right) \\ f\left( n,m,k \right) =f\left( n,m,k \right) +f\left( n-\text{1,}m,k+1 \right) \left( \text{原串第}m\text{位置不是右括号} \right) \\ \text{最后答案放置了}2\times n\text{个字符,使用了原串的全部字符,左括号比右括号多0个的方案数} $$

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <inttypes.h>
    #include <string>
    #include <map>
    #include <cstring>
    #define debug(x) std::cout << #x << " = " << x << std::endl;
    
    typedef long long int int_t;
    
    using std::cin;
    using std::endl;
    using std::cout;
    
    const int_t mod = 1000000007;
    const int_t LARGE = 300;
    char buff[LARGE + 10];
    int_t val[LARGE + 1];
    int_t prefix[LARGE + 1];
    int_t dp[LARGE + 1][LARGE + 1][LARGE + 1];
    int_t n;
    int_t len;
    
    void inc(int_t& x,int_t p) {
    	x = (x % mod + p + 2 * mod) % mod;
    }
    
    int main() {
    	freopen("regular.in","r",stdin);
    	freopen("regular.out","w",stdout);
    
    	cin >> n;
    	scanf("%s",buff + 1);
    	len = strlen(buff + 1);
    	for(int_t i = 1; i <= len; i++) {
    		if(buff[i] == '(') val[i] = 1;
    		else val[i] = -1;
    		prefix[i] = prefix[i - 1] + val[i];
    	}
    	dp[0][0][0] = 1;
    	for(int_t i = 1; i <= 2 * n; i++) {
    		for(int_t j = 0; j <= std::min(i,len); j++) {
    			for(int_t k = 0; k <= i; k++) {
    				//×óÀ¨ºÅ
    				if(val[j]==1&&k-1>=0&&j>0){
    					inc(dp[i][j][k],dp[i-1][j-1][k-1]);
    				} else{
    					if(k-1>=0)inc(dp[i][j][k],dp[i-1][j][k-1]);
    				}
    				//ÓÒÀ¨ºÅ 
    				if(val[j]==-1&&j>0){
    					inc(dp[i][j][k],dp[i-1][j-1][k+1]);
    				} else{
    					inc(dp[i][j][k],dp[i-1][j][k+1]);
    				}
    				continue;
    			}
    		}
    	}
    	cout<<dp[2 * n][len][0]<<endl;
    	return 0;
    }
    
  • Luogu1373 小a和uim之大逃离

    $$ \text{设}f\left( n,m,k,d \right) \text{表示走到了格子}\left( n,m \right) \text{,} \\ \text{小}a\text{采集的魔液与}uim\text{采集的魔夜的差为}k\text{,这一步是小}a\text{还是}uim\text{采集的方案数} \\ \text{初始时,}f\left( n,m,mat_{n,m},1 \right) =\text{1\ }\left( \text{要求可以从任意点出发} \right) \\ \text{转移时} \\ f\left( n+\text{1,}m,k+mat_{n+\text{1,}m},0 \right) +=f\left( n,m,k,1 \right) \\ f\left( n,m+\text{1,}k+mat_{n,m+1},0 \right) +=f\left( n,m,k,1 \right) \\ f\left( n+\text{1,}m,k-mat_{n+\text{1,}m},1 \right) +=f\left( n,m,k,0 \right) \\ f\left( n,m+\text{1,}k-mat_{n,m+1},1 \right) +=f\left( n,m,k,0 \right) \\ \text{最后答案为所有}f\left( n,m,\text{0,}1 \right) \text{的和。} \\ $$

    #include <iostream>
    using int_t = int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t mod = 1000000007;
    
    int_t n, m, k;
    //走到了n行m列,a与uim的差为k,最后一步0:a,1:uim的方案数
    int_t f[802][802][16][2];
    int_t mat[802][802];
    
    inline void inc(int_t &x, int_t b)
    {
        x = ((x + b) % mod + mod) % mod;
    }
    
    int main()
    {
        scanf("%d%d%d", &n, &m, &k);
        k += 1;
        for (int_t i = 1; i <= n; i++)
        {
            for (int_t j = 1; j <= m; j++)
            {
                scanf("%d", &mat[i][j]);
                f[i][j][mat[i][j]][0] = 1;
            }
        }
        int_t result = 0;
        for (int_t i = 1; i <= n; i++)
        {
            for (int_t j = 1; j <= m; j++)
            {
                for (int_t k = 0; k < ::k; k++)
                {
                    //下一步是a
                    inc(f[i + 1][j][(k + mat[i + 1][j]) % ::k][0], (f[i][j][k][1]));
                    inc(f[i][j + 1][(k + mat[i][j + 1]) % ::k][0], (f[i][j][k][1]));
                    //下一步是uim
                    inc(f[i + 1][j][(k - mat[i + 1][j] + ::k) % ::k][1], f[i][j][k][0]);
                    inc(f[i][j + 1][(k - mat[i][j + 1] + ::k) % ::k][1], f[i][j][k][0]);
                }
                inc(result, f[i][j][0][1]);
            }
        }
        printf("%d", result);
        return 0;
    }