标签: 卷积

  • 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;
    }

     

  • CFGYM 102823 CCPC2018 桂林 B题 Array Modify

    题倒是还行,达标找规律找出来结论,然后就想到了一个多项式快速幂的做法。

    一开始写的两个log的快速幂,TLE。

    然后尝试改成exp+log,跑得看起来快了,但是遇到多组数据(n的大小递增的时候)就挂掉了?

    仔细调查后发现源于自己一直以来exp函数内一直存在的错误:

    exp内调用了log,log内调用了inv,exp内给G0(用以存储log返回值的数组)预留了upper2n(2*n)的空间,但是inv内却使用了upper2n(3*n+1)的空间!

    (调试方法:数组切换为动态分配并开启内存检查)

    对于只计算exp一次而言,这个错误不会造成影响,但是计算多次的时候由于空间污染会导致错误结果。

    解决方法:在exp内也预留upper2n(3*n)的空间,通过本题。

    另外小测试了一下,两个log的多项式快速幂跑本题极限数据,NTT变换的多项式总长度为“27262976”,而exp+log实现的快速幂,NTT变换的多项式总长度为 19922408,我原本以为因为常数原因,exp+log会跑得更慢,没想到事实并非如此。

     

    // luogu-judger-enable-o2
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    const int mod = 998244353;
    const int g = 3;
    const int LARGE = 1 << 20;
    int revs[21][LARGE + 1];
    
    int power(int base, int index);
    void transform(int* A, int len, int arg);
    void poly_inv(const int* A, int n, int* result);
    void poly_log(const int* A, int n, int* result);
    void poly_exp(const int* A, int n, int* result);
    
    std::vector transform_count;
    #ifdef TDEBUG
    #define TRANSFORM_DEBUG(n) \
        { transform_count.push_back(n); }
    #else
    #define TRANSFORM_DEBUG(n)
    #endif
    int bitRev(int base, int size2) {
        int result = 0;
        for (int i = 1; i < size2; i++) {
            result |= (base & 1);
            base >>= 1;
            result <<= 1;
        }
        result |= (base & 1);
        return result;
    }
    int upper2n(int x) {
        int result = 1;
        while (result < x)
            result *= 2;
        return result;
    }
    int main() {
        std::ios::sync_with_stdio(false);
    
        for (int i = 0; (1 << i) <= LARGE; i++) {
            for (int j = 0; j < LARGE; j++) {
                revs[i][j] = bitRev(j, i);
            }
        }
        static int F[LARGE + 1], G[LARGE + 1];
        static int arr[LARGE + 1];
        int T;
        cin >> T;
        for (int_t _i = 1; _i <= T; _i++) {
            memset(F, 0, sizeof(F));
            memset(G, 0, sizeof(G));
            memset(arr, 0, sizeof(arr));
            int_t n, L, m;
            cin >> n >> L >> m;
    
            for (int_t i = 1; i <= n; i++)
                cin >> arr[i];
            int size2 = upper2n(4 * (n + 1));
            for (int i = 0; i < size2; i++) {
                if (i < L)
                    F[i] = 1;
                else
                    F[i] = 0;
                G[i] = 0;
            }
    #ifdef DEBUG
            cout << "init = " << endl;
            cout << "F = ";
            for (int_t i = 0; i < 2 * n; i++)
                cout << F[i] << " ";
            cout << endl;
    
            cout << "G = ";
            for (int_t i = 0; i < 2 * n; i++)
                cout << G[i] << " ";
            cout << endl;
    #endif
    
            poly_log(F, n, G);
    #ifdef DEBUG
            cout << "after log = " << endl;
            cout << "G = ";
            for (int_t i = 0; i < 2 * n; i++)
                cout << G[i] << " ";
            cout << endl;
    #endif
            for (int i = 0; i < n; i++)
                G[i] = G[i] * m % mod;
            for (int i = 0; i < size2; i++)
                F[i] = 0;
    #ifdef DEBUG
            cout << "before exp = " << endl;
            cout << "G = ";
            for (int_t i = 0; i < 2 * n; i++)
                cout << G[i] << " ";
            cout << endl;
    #endif
            poly_exp(G, n, F);
    #ifdef DEBUG
            cout << "after exp = " << endl;
            cout << "F = ";
            for (int_t i = 0; i < 2 * n; i++)
                cout << F[i] << " ";
            cout << endl;
    #endif
    
            for (int i = 0; i < size2; i++) {
                if (i >= n)
                    F[i] = 0;
                if (i >= n && i < 2 * n)
                    G[i] = arr[i - n + 1];
                else
                    G[i] = 0;
            }
            std::reverse(G + n, G + 2 * n);
    
    #ifdef DEBUG
            cout << "F = ";
            for (int_t i = 0; i < 2 * n; i++)
                cout << F[i] << " ";
            cout << endl;
    
            cout << "G = ";
            for (int_t i = 0; i < 2 * n; i++)
                cout << G[i] << " ";
            cout << endl;
    #endif
    
            transform(F, size2, 1);
            transform(G, size2, 1);
            for (int i = 0; i < size2; i++)
                F[i] = (int_t)F[i] * G[i] % mod;
            transform(F, size2, -1);
            int inv = power(size2, -1);
            std::reverse(F + n, F + 2 * n);
            cout << "Case " << _i << ": ";
    
            for (int i = n; i < 2 * n; i++) {
                cout << (int_t)F[i] * inv % mod << " ";
            }
            cout << endl;
    #ifdef DEBUG
            for (int i = 0; i < n; i++) {
                cout << "F " << i << " = " << F[i] << endl;
            }
    #endif
    #ifdef TDEBUG
            for (auto x : transform_count) {
                cout << x << " ";
            }
            cout << endl;
    #endif
        }
        return 0;
    }
    
    int power(int base, int index) {
        const int phi = mod - 1;
        index = (index % phi + phi) % phi;
        base = (base % mod + mod) % mod;
        int result = 1;
        while (index) {
            if (index & 1)
                result = (int_t)result * base % mod;
            base = (int_t)base * base % mod;
            index >>= 1;
        }
        return result;
    }
    void transform(int* A, int len, int arg) {
        TRANSFORM_DEBUG(len);
        int size2 = int(log2(len) + 0.5);
        for (int i = 0; i < len; i++) {
            int x = revs[size2][i];
            if (x > i)
                std::swap(A[i], A[x]);
        }
        for (int i = 2; i <= len; i *= 2) {
            int mr = power(g, (int_t)arg * (mod - 1) / i);
            for (int j = 0; j < len; j += i) {
                int curr = 1;
                for (int k = 0; k < i / 2; k++) {
                    int u = A[j + k];
                    int t = (int_t)A[j + k + i / 2] * curr % mod;
                    A[j + k] = (u + t) % mod;
                    A[j + k + i / 2] = (u - t + mod) % mod;
                    curr = (int_t)curr * mr % mod;
                }
            }
        }
    }
    //计算多项式A在模x^n下的逆元
    // C(x)<-2B(x)-A(x)B^2(x)
    void poly_inv(const int* A, int n, int* result) {
        if (n == 1) {
            result[0] = power(A[0], -1);
            return;
        }
        poly_inv(A, n / 2 + n % 2, result);
        static int temp[LARGE + 1];
        int size2 = upper2n(3 * n + 1);
        for (int i = 0; i < size2; i++) {
            if (i < n)
                temp[i] = A[i];
            else
                temp[i] = 0;
        }
        transform(temp, size2, 1);
        transform(result, size2, 1);
        for (int i = 0; i < size2; i++) {
            result[i] =
                ((int_t)2 * result[i] % mod -
                 (int_t)temp[i] * result[i] % mod * result[i] % mod + 2 * mod) %
                mod;
        }
        transform(result, size2, -1);
        for (int i = 0; i < size2; i++) {
            if (i < n) {
                result[i] = (int_t)result[i] * power(size2, -1) % mod;
            } else {
                result[i] = 0;
            }
        }
    }
    void poly_log(const int* A, int n, int* result) {
        int size2 = upper2n(3 * n + 1);
        static int Ad[LARGE + 1];
        for (int i = 0; i < size2; i++) {
            if (i < n) {
                Ad[i] = (int_t)(i + 1) * A[i + 1] % mod;
            } else {
                Ad[i] = 0;
            }
            result[i] = 0;
        }
        transform(Ad, size2, 1);
        poly_inv(A, n, result);
        transform(result, size2, 1);
        for (int i = 0; i < size2; i++) {
            result[i] = (int_t)result[i] * Ad[i] % mod;
        }
        transform(result, size2, -1);
        for (int i = 0; i < size2; i++)
            if (i < n)
                result[i] = (int_t)result[i] * power(size2, -1) % mod;
            else
                result[i] = 0;
        for (int i = n - 1; i >= 1; i--) {
            result[i] = (int_t)result[i - 1] * power(i, -1) % mod;
        }
        result[0] = 0;
    }
    void poly_exp(const int* A, int n, int* result) {
        if (n == 1) {
            assert(A[0] == 0);
            result[0] = 1;
            return;
        }
        poly_exp(A, n / 2 + n % 2, result);
        static int Ax[LARGE + 1];
        // memset(G0, 0, sizeof(G0));
        // memset(Ax,0,sizeof(Ax));
        int size2 = upper2n(3 * n + 1);
        // int* G0 = new int[size2];
        static int G0[LARGE + 1];
        for (int_t i = 0; i < size2; i++) {
            if (i < n)
                Ax[i] = A[i];
            else
                Ax[i] = 0;
            G0[i] = 0;
        }
        poly_log(result, n, G0);
    
        transform(Ax, size2, 1);
        transform(G0, size2, 1);
        transform(result, size2, 1);
        for (int i = 0; i < size2; i++)
            result[i] =
                (result[i] - (int_t)result[i] * (G0[i] - Ax[i] + mod) % mod + mod) %
                mod;
        transform(result, size2, -1);
        for (int i = 0; i < size2; i++)
            if (i < n)
                result[i] = (int_t)result[i] * power(size2, -1) % mod;
            else
                result[i] = 0;
        // delete[] G0;
    }
    
  • 洛谷4173 残缺的字符串

    $$ \text{这东西好神奇啊}qwq \\ \text{求出字符串}B\text{在字符串}A\text{中所有出现的位置。} \\ \text{设字符串下标均从0开始} \\ \text{设}A_x\text{表示}A\text{的第}x\text{个字符。} \\ \text{来定义一个神奇的函数,称之为匹配函数} \\ \text{设}m\text{为}B\text{串的长度} \\ P\left( x \right) =\sum_{0\le i<m}{\left( \begin{array}{c} A_{x-m+1+i}-B_i\\ \end{array} \right) ^2} \\ \text{注意到这个函数当且仅当字符串}B\text{出现在串}A\text{以}x\text{结尾的位置时才为}0 \\ \text{暴力计算起来仍然是}O\left( n^2 \right) \text{的} \\ \text{考虑拆开} \\ P\left( x \right) =\sum_{0\le i<m}{\begin{array}{c} B_i^2+A_{x-m+1+i}^2-2B_iA_{x-m+1+i}\\ \end{array}} \\ =\sum_{0\le i<m}{B_i^2}+\sum_{0\le i<m}{A_{x-m+1+i}^2}-2\sum_{0\le i<m}{B_iA_{x-m+1+i}} \\ \text{前两个求和符号可以直接计算。} \\ \text{第三个怎么搞?} \\ \text{设}T=B^R,\text{即}T\text{是}B\text{的反串。} \\ \text{所以存在}T_x=B_{m-x-1} \\ \text{故}P\left( x \right) =\sum_{0\le i<m}{T_i^2}+\sum_{0\le i<m}{A_{x-m+1+i}^2}-2\sum_{0\le i<m}{T_{m-i-1}A_{x-m+1+i}} \\ \text{注意到一个细节,}\left( m-i-1 \right) +\left( x-m+1+i \right) =x \\ \text{所以我们可以强行凑成卷积} \\ \sum_{0\le i<m}{T_{m-i-1}A_{x-m+1+i}} \\ =\sum_{0<m-i\le m}{T_{m-i-1}A_{x-m+1+i}} \\ =\sum_{0<i\le m}{T_{i-1}A_{x-i+1}} \\ =\sum_{-1<i-1\le m-1}{T_{i-1}A_{x-i+1}} \\ =\sum_{0\le i\le m-1}{T_iA_{x-i}} \\ =\sum_{i+j=x}{T_iA_j} \\ P\left( x \right) =\sum_{0\le i<m}{T_i^2}+\sum_{0\le i<m}{A_{x-i}^2}-2\sum_{i+j=x}{T_iA_j} \\ \text{当}x\ge m-\text{1的时候,}\sum_{0\le i\le m-1}{T_iA_{x-i}}=\sum_{0\le i\le x}{T_iA_{x-i}} \\ \text{然后可以卷积了}qwq \\ \text{带通配符怎么办?} \\ \text{设通配符的字符代码为}0 \\ \text{匹配函数变成}P\left( x \right) =\sum_{0\le i<m}{\left( \begin{array}{c} A_{x-m+1+i}-B_i\\ \end{array} \right) ^2A_{x-m+1+i}B_i} \\ =\sum_{0\le i<m}{\left( B_i^2+A_{x-m+1+i}^2-2B_iA_{x-m+1+i} \right) A_{x-m+1+i}B_i} \\ =\sum_{0\le i<m}{B_{i}^{3}A_{x-m+1+i}}+\sum_{0\le i<m}{B_iA_{x-m+1+i}^3}-2\sum_{0\le i<m}{A_{x-m+1+i}^2B_i^2} \\ B_x=T_{m-x-1} \\ P\left( x \right) =\sum_{0\le i<m}{T_{m-i-1}^3A_{x-m+1+i}}+\sum_{0\le i<m}{T_{m-i-1}A_{x-m+1+i}^3}-2\sum_{0\le i<m}{A_{x-m+1+i}^2T_{m-i-1}^2} \\ P\left( x \right) =\sum_{i+j=x}{T_i^3A_j}+\sum_{i+j=x}{T_iA_j^3}-2\sum_{i+j=x}{A_i^2T_j^2} \\ \text{仍然大力卷积即可。} \\ $$

    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <iostream>
    #include <vector>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t mod = 998244353;
    const int_t g = 3;
    const int_t LARGE = 1 << 20;
    int_t power(int_t base, int_t index);
    void transform(int_t* A, int_t size, int_t arg = 1);
    int_t bitRev(int_t x, int_t size2) {
        int_t result = 0;
        for (int_t i = 1; i < size2; i++) {
            result |= (x & 1);
            x >>= 1;
            result <<= 1;
        }
        return result | (x & 1);
    }
    constexpr inline int_t upper2n(int_t x) {
        int_t result = 1;
        while (result < x) result *= 2;
        return result;
    }
    int main() {
        int n, m;
        static char As[LARGE], Bs[LARGE], Ts[LARGE];
        scanf("%d%d%s%s", &m, &n, Bs, As);
        std::reverse_copy(Bs, Bs + m, Ts);
        // cout << Ts << endl;
        static int_t A1[LARGE], A2[LARGE], A3[LARGE];
        static int_t T1[LARGE], T2[LARGE], T3[LARGE];
        for (int_t i = 0; i < n; i++) {
            A1[i] = As[i];
            if (As[i] == '*') A1[i] = 0;
            A2[i] = A1[i] * As[i];
            A3[i] = A2[i] * As[i];
        }
        for (int_t i = 0; i < m; i++) {
            T1[i] = Ts[i];
            if (Ts[i] == '*') T1[i] = 0;
            T2[i] = T1[i] * Ts[i];
            T3[i] = T2[i] * Ts[i];
        }
        int_t size = upper2n(n + m + 1);
        transform(A1, size);
        transform(A2, size);
        transform(A3, size);
        transform(T1, size);
        transform(T2, size);
        transform(T3, size);
        static int_t result[LARGE];
        for (int_t i = 0; i < size; i++) {
            result[i] = (T3[i] * A1[i] % mod + T1[i] * A3[i] % mod -
                         2 * A2[i] * T2[i] % mod + 3 * mod) %
                        mod;
        }
        transform(result, size, -1);
        for (int_t i = 0; i < size; i++) {
            result[i] = result[i] * power(size, -1) % mod;
            // cout << "pos " << i << " " << result[i] << endl;
        }
        std::vector<int> results;
        for (int_t i = 0; i < n; i++) {
            if (result[i] == 0 && (i - m + 2 >= 1)) {
                results.push_back(i - m + 2);
            }
        }
        printf("%d\n", (int)results.size());
        for (int x : results) printf("%d ", x);
        return 0;
    }
    
    int_t power(int_t base, int_t index) {
        const auto phi = mod - 1;
        index = (index % phi + phi) % phi;
        base = (base % mod + mod) % mod;
        int_t result = 1;
        while (index) {
            if (index & 1) result = result * base % mod;
            index >>= 1;
            base = base * base % mod;
        }
        return result;
    }
    void transform(int_t* A, int_t size, int_t arg) {
        int_t size2 = log2(size);
        for (int_t i = 0; i < size; i++) {
            int_t x = bitRev(i, size2);
            if (x > i) std::swap(A[i], A[x]);
        }
        for (int_t i = 2; i <= size; i *= 2) {
            int_t mr = power(g, arg * (mod - 1) / i);
            for (int_t j = 0; j < size; j += i) {
                int_t curr = 1;
                for (int_t k = 0; k < i / 2; k++) {
                    int_t u = A[j + k];
                    int_t t = A[j + k + i / 2] * curr % mod;
                    A[j + k] = (u + t) % mod;
                    A[j + k + i / 2] = (u - t + mod) % mod;
                    curr = curr * mr % mod;
                }
            }
        }
    }

     

  • TJOI2016 求和

    $$ \text{题意:} \\ \text{求}f\left( n \right) =\sum_{0\le i\le n}{\sum_{0\le j\le i}{S_2\left( i,j \right) \times 2^j\times j!}} \\ \text{第二类斯特林数的通项公式:} \\ S\left( i,j \right) =\frac{1}{j!}\sum_{0\le k\le j}{\left( \begin{array}{c} j\\ k\\ \end{array} \right) \left( -1 \right) ^{j-k}k^i} \\ \text{代入,因为}j>i\text{时,}S_2\left( i,j \right) \text{为0,所以求和上界可以放到}n \\ f\left( n \right) =\sum_{0\le j\le n}{2^j\times j!\sum_{0\le i\le n}{S\left( i,j \right)}} \\ f\left( n \right) =\sum_{0\le j\le n}{2^j\sum_{0\le i\le n}{\sum_{0\le k\le j}{\left( \begin{array}{c} j\\ k\\ \end{array} \right) \times \left( -1 \right) ^{j-k}k^i}}} \\ \text{整理一下} \\ f\left( n \right) =\sum_{0\le j\le n}{j!\sum_{0\le k\le j}{\frac{2^j}{k!\left( j-k \right) !}\times \left( -1 \right) ^{j-k}}\sum_{0\le i\le n}{k^i}} \\ \text{注意,}\sum_{0\le i\le n}{k^i}\text{可以直接用等比数列求和公式}O\left( 1 \right) \text{计算!} \\ =\sum_{0\le j\le n}{j!\times 2^j\sum_{0\le k\le j}{\frac{\sum_{0\le i\le n}{k^i}}{k!}\times \frac{\left( -1 \right) ^{j-k}}{\left( j-k \right) !}}} \\ \text{这个式子可以卷积} \\ f\left( n \right) =\sum_{0\le j\le n}{j!\sum_{0\le k\le j}{\frac{\left( -1 \right) ^k\sum_{0\le i\le n}{k^i}}{k!}\times \frac{1}{\left( j-k \right) !}}} \\ \\ $$

     

    #include <iostream>
    #include <algorithm>
    #include <cmath>
    using std::cin;
    using std::cout;
    using std::endl;
    
    using int_t = long long int;
    
    const int_t mod = 998244353;
    const int_t g = 3;
    const int_t LARGE = 1 << 20;
    
    int_t power(int_t base, int_t index);
    int_t bitRev(int_t x, int_t size2);
    template <int_t arg = 1>
    void transform(int_t *A, int_t size);
    int_t upper2n(int_t x);
    
    int_t sum(int_t k, int_t n)
    {
        if (k == 1)
            return (n + 1) % mod;
        // if(k==0) return 0;s
        return ((power(k, n + 1) - 1) % mod * power(k - 1, -1) % mod) % mod;
    }
    
    int main()
    {
        int_t n;
        cin >> n;
        static int_t fact[LARGE] = {1};
        for (int_t i = 1; i <= n * 2; i++)
        {
            fact[i] = (fact[i - 1] * i) % mod;
        }
        static int_t A[LARGE];
        static int_t B[LARGE];
        int_t size = upper2n(2 * n + 1);
        for (int_t i = 0; i <= n; i++)
        {
            A[i] = ((sum(i, n)) % mod * power(fact[i], -1) % mod + mod) % mod;
            B[i] = power(fact[i], -1) * power(-1, i) % mod;
            // cout<<A[i]<<" ";
        }
        transform(A, size);
        transform(B, size);
        for (int_t i = 0; i <= size; i++)
            A[i] = A[i] * B[i] % mod;
        transform<-1>(A, size);
        int_t result = 0;
        for (int_t i = 0; i <= n; i++)
        {
            A[i] = A[i] * power(size, -1) % mod * fact[i] % mod * power(2, i) % mod;
            result = (result + A[i]) % mod;
        }
        cout << result << endl;
        return 0;
    }
    
    template <int_t arg = 1>
    void transform(int_t *A, int_t size)
    {
        int_t size2 = log2(size);
        for (int_t i = 0; i < size; i++)
        {
            int_t x = bitRev(i, size2);
            if (x > i)
                std::swap(A[i], A[x]);
        }
        for (int_t i = 2; i <= size; i *= 2)
        {
            int_t mr = power(g, arg * (mod - 1) / i);
            for (int_t j = 0; j < size; j += i)
            {
                int_t curr = 1;
                for (int_t k = 0; k < i / 2; k++)
                {
                    int_t u = A[j + k];
                    int_t t = A[j + k + i / 2] * curr % mod;
                    A[j + k] = (u + t) % mod;
                    A[j + k + i / 2] = (u - t + mod) % mod;
                    curr = (curr * mr) % mod;
                }
            }
        }
    }
    
    int_t upper2n(int_t x)
    {
        int_t result = 1;
        while (result < x)
            result *= 2;
        return result;
    }
    
    int_t bitRev(int_t x, int_t size2)
    {
        int_t result = 0;
        for (int_t i = 1; i < size2; i++)
        {
            result |= (x & 1);
            x >>= 1;
            result <<= 1;
        }
        return result | (x & 1);
    }
    int_t power(int_t base, int_t index)
    {
        base = (base % mod + mod) % mod;
        if (index < 0)
        {
            index *= -1;
            base = power(base, mod - 2);
        }
        int_t result = 1;
        while (index)
        {
            if (index & 1)
            {
                result = result * base % mod;
            }
            base = base * base % mod;
            index >>= 1;
        }
        return result;
    }