[多项式] 洛谷P7493 旅人1969

题目描述

传送门

一条笔直的公路上有 \(n\) 个旅店,第 \(i\) 个旅店的坐标是 \(i\),每一天早上从旅店出发走最多 \(m\) 个距离,同时固定给你一个常数 \(k\)

给定 \(q\) 组询问,每次给定 \(u,v\),求早上从旅店 \(u\) 出发到旅店 \(v\),途径不超过 \(k\) 个旅店(不含起点 \(u\))且行走方向不变的方案数。两种方案不同当且仅当存在一个不同的旅店选择,答案对 \(998244353\) 取模。

对于所有数据,\(n,q\leq 10^5\)\(m,k\leq 10^4\)\(mk\leq 10^5\)\(u,v\leq n\)

题解

\(dp[k][n]\) 表示间隔 \(n\) 个距离,在 \(k\) 个旅店停留的方案数。则有

\[dp[k][n]=dp[k][n-1]+dp[k-1][n-1]-dp[k-1][n-m-1] \]

\(F_k\)\(dp[k][n]\) 的普通型生成函数,即 \(F_k=\sum_{n=0}^{\infty}dp[k][n]x^n\),则

\[F_k=xF_k+xF_{k-1}-x^{m+1}F_{k-1}\\ (1-x)F_k=x(1-x^m)F_{k-1}\\ F_k=\frac{x(1-x^m)F_{k-1}}{1-x}\\ F_k=\left(\frac{x(1-x^m)}{1-x}\right)^k \]

\(ans=[x^n]\sum_{k=0}^KF_k\)

\[ans=[x^n]\sum_{k=0}^KF_k=[x^n]\sum_{k=0}^K\left(\frac{x(1-x^m)}{1-x}\right)^k=[x^n]\frac{1-\left(\frac{x(1-x^m)}{1-x}\right)^{K+1}}{1-\frac{x(1-x^m)}{1-x}}\\ =[x^n]\frac{(1-x)^{K+1}-x^{K+1}(1-x^m)^{K+1}}{(1-x)^{K+1}-x(1-x^m)(1-x)^K}=[x^n]\frac{(1-x)^{K+1}-(x-x^{m+1})^{K+1}}{(1-x)^K[(1-x)-x(1-x^m)]}\\ =[x^n]\frac{(1-x)^{K+1}-(x-x^{m+1})^{K+1}}{(1-x)^K(1-2x+x^{m+1})}\\ \]

注意到分子分母的最高次项都是 \(O(mk)\) 级别的,而 \(mk\leq 10^5\),所以可以使用二项式定理把分子分母展开,对分母作多项式求逆,再和分子作卷积,即可求得 \(n\) 次项前的系数。时间复杂度 \(O(mk\log mk)\)

Code

#include <bits/stdc++.h>
using namespace std;

#define RG register int
#define LL long long

template<typename elemType>
inline void Read(elemType& T) {
    elemType X = 0, w = 0; char ch = 0;
    while (!isdigit(ch)) { w |= ch == '-';ch = getchar(); }
    while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
    T = (w ? -X : X);
}

LL ExPow(LL b, LL n, LL MOD) {//b^n mod MOD
    if (MOD == 1) return 0;
    LL x = 1;
    LL Power = b % MOD;
    while (n) {
        if (n & 1) x = x * Power % MOD;
        Power = Power * Power % MOD;
        n >>= 1;
    }
    return x;
}

const int maxn = 270000;

namespace Poly {
    int r[maxn];
    LL buf[maxn], buf1[maxn], buf2[maxn], buf3[maxn], buf4[maxn];
    int L, limit;
    const LL P = 998244353, G = 3, Gi = 332748118;

    LL pinv(LL x) { return ExPow(x, P - 2, P); }

    //快速数论变换 type=1:正变换 type=-1:逆变换
    void NTT(LL* A, int type) {
        for (int i = 0; i < limit; i++)
            if (i < r[i]) swap(A[i], A[r[i]]);
        for (int mid = 1; mid < limit; mid <<= 1) {
            LL Wn = ExPow(type == 1 ? G : Gi, (P - 1) / (mid << 1), P);
            for (int j = 0; j < limit; j += (mid << 1)) {
                LL w = 1;
                for (int k = 0; k < mid; k++, w = (w * Wn) % P) {
                    int x = A[j + k], y = w * A[j + k + mid] % P;
                    A[j + k] = (x + y) % P;
                    A[j + k + mid] = (x - y + P) % P;
                }
            }
        }
        if (type == 1) return;
        LL inv_limit = pinv(limit);
        for (int i = 0; i < limit; ++i)
            A[i] = A[i] * inv_limit % P;
    }

    //多项式卷积 a(x): N-1次多项式 b(x): M-1次多项式
    void Conv(LL* A, int N, LL* B, LL M, LL* C) {
        L = 0; limit = 1;
        while (limit <= N + M) limit <<= 1, L++;
        for (int i = 0; i < limit; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (L - 1));
        NTT(A, 1); NTT(B, 1);
        for (int i = 0; i < limit; i++) C[i] = A[i] * B[i] % P;
        NTT(C, -1);
    }

    //多项式求逆 F*G=1 mod x^n
    void PolyInv(LL* F, LL* G, int N) {
        if (N == 1) { G[0] = pinv(F[0]);return; }
        PolyInv(F, G, (N + 1) >> 1);
        for (limit = 1; limit <= (N << 1); limit <<= 1);
        for (int i = 0; i < limit; ++i) {
            r[i] = (r[i >> 1] >> 1) | ((i & 1) ? (limit >> 1) : 0);
            buf[i] = (i < N ? F[i] : 0);
        }
        NTT(buf, 1);NTT(G, 1);
        for (int i = 0; i < limit; ++i)
            G[i] = (2LL - buf[i] * G[i] % P + P) % P * G[i] % P;
        NTT(G, -1);
        fill(G + N, G + limit, 0);
    }
}

LL F[maxn], G[maxn], H[maxn];
int N, M, K, Q;

LL Inv[100010], Fact[100010], FInv[100010];
const LL MOD = 998244353LL;

void Init(int n) {
    Inv[1] = Fact[0] = Fact[1] = FInv[0] = FInv[1] = 1;
    for (int i = 2;i <= n;++i) {
        Inv[i] = ((-(MOD / i) * Inv[MOD % i]) % MOD + MOD) % MOD;
        Fact[i] = Fact[i - 1] * i % MOD;
        FInv[i] = FInv[i - 1] * Inv[i] % MOD;
    }
}

inline LL C(LL n, LL k) {
    return Fact[n] * FInv[k] % MOD * FInv[n - k] % MOD;
}

int main() {
    Read(N);Read(M);Read(K);Read(Q);
    Init(K + 1);
    for (int i = 0;i <= K + 1;++i) {
        if (i & 1) F[i] = (F[i] + (MOD - C(K + 1, i)) % MOD) % MOD;
        else F[i] = (F[i] + C(K + 1, i)) % MOD;
        if ((K + 1 - i) & 1)
            F[(M + 1) * (K + 1 - i) + i] = (F[(M + 1) * (K + 1 - i) + i] + C(K + 1, i)) % MOD;
        else
            F[(M + 1) * (K + 1 - i) + i] = ((F[(M + 1) * (K + 1 - i) + i] - C(K + 1, i)) % MOD + MOD) % MOD;
    }
    for (int i = 0;i <= K;++i) {
        if (i & 1) {
            G[i] = ((G[i] - C(K, i)) % MOD + MOD) % MOD;
            G[i + 1] = (G[i + 1] + C(K, i) * 2LL % MOD) % MOD;
            G[i + M + 1] = ((G[i + M + 1] - C(K, i)) % MOD + MOD) % MOD;

        }
        else {
            G[i] = (G[i] + C(K, i)) % MOD;
            G[i + 1] = ((G[i + 1] - C(K, i) * 2LL % MOD) % MOD + MOD) % MOD;
            G[i + M + 1] = (G[i + M + 1] + C(K, i)) % MOD;
        }
    }
    Poly::PolyInv(G, H, N);
    Poly::Conv(F, (M + 1) * (K + 1) + 1, H, N, F);
    while(Q--){
        int u, v;Read(u);Read(v);
        printf("%lld\n", F[v - u]);     
    }
    return 0;
}
posted @ 2021-04-10 10:37  AE酱  阅读(164)  评论(1编辑  收藏  举报