Cells(2021牛客暑期多校训练营9 C,LGV引理 + 范德蒙德行列式 + NTT)

一、题目链接

Cells

二、题目大意

在一个二维平面内,有 n n n 个起点 ( 0 , a i ) (0, a_i) (0,ai) 要走到对应的终点 ( i , 0 ) (i, 0) (i,0),每次可以向下走或向左走,问不相交路径组的方案数.

1 ≤ n ≤ 5 × 1 0 5 , 0 ≤ a i ≤ 1 0 6 , a i < a i + 1 1 \leq n \leq 5 \times 10^5, 0 \leq a_i \leq 10^6, a_i < a_{i+1} 1n5×105,0ai106,ai<ai+1.

三、分析

易知从 ( 0 , a i ) (0, a_i) (0,ai) 走到 ( j , 0 ) (j,0) (j,0) 的方案数为 ( a i + j j ) \binom{a_i + j}{j} (jai+j).
M = [ ( a 1 + 1 1 ) ( a 1 + 2 2 ) ⋯ ( a 1 + n n ) ( a 2 + 1 1 ) ( a 2 + 2 2 ) ⋯ ( a 2 + n n ) ⋮ ⋮ ⋯ ⋮ ( a n + 1 1 ) ( a n + 2 2 ) ⋯ ( a n + n n ) ] M = \left [ \begin{matrix} \binom{a_1 + 1}{1} & \binom{a_1 + 2}{2} & \cdots & \binom{a_1 + n}{n} \\ \binom{a_2 + 1}{1} & \binom{a_2 + 2}{2} & \cdots & \binom{a_2 + n}{n} \\ \vdots & \vdots & \cdots & \vdots \\ \binom{a_n + 1}{1} & \binom{a_n + 2}{2} & \cdots & \binom{a_n + n}{n} \end{matrix} \right] M=(1a1+1)(1a2+1)(1an+1)(2a1+2)(2a2+2)(2an+2)(na1+n)(na2+n)(nan+n)
则由 LGV 引理可知 ∣ M ∣ |M| M 即为答案.

由于 m a x ( n ) = 5 × 1 0 5 max(n) = 5 \times 10^5 max(n)=5×105,直接高斯消元是无法承受的,考虑简便计算 ∣ M ∣ |M| M 的方法.
∣ M ∣ = ∣ ( a 1 + 1 1 ) ( a 1 + 2 2 ) ⋯ ( a 1 + n n ) ( a 2 + 1 1 ) ( a 2 + 2 2 ) ⋯ ( a 2 + n n ) ⋮ ⋮ ⋯ ⋮ ( a n + 1 1 ) ( a n + 2 2 ) ⋯ ( a n + n n ) ∣ = ∣ ( a 1 + 1 ) ! 1 ! a 1 ! ( a 1 + 2 ) ! 2 ! a 1 ! ⋯ ( a 1 + n ) ! n ! a 1 ! ( a 2 + 1 ) ! 1 ! a 2 ! ( a 2 + 2 ) ! 2 ! a 2 ! ⋯ ( a 2 + n ) ! n ! a 2 ! ⋮ ⋮ ⋯ ⋮ ( a n + 1 ) ! 1 ! a n ! ( a n + 2 ) ! 2 ! a n ! ⋯ ( a n + n ) ! n ! a n ! ∣ = ∏ i = 1 n 1 i ! ∣ ( a 1 + 1 ) ! a 1 ! ( a 1 + 2 ) ! a 1 ! ⋯ ( a 1 + n ) ! a 1 ! ( a 2 + 1 ) ! a 2 ! ( a 2 + 2 ) ! a 2 ! ⋯ ( a 2 + n ) ! a 2 ! ⋮ ⋮ ⋯ ⋮ ( a n + 1 ) ! a n ! ( a n + 2 ) ! a n ! ⋯ ( a n + n ) ! a n ! ∣ = ∏ i = 1 n 1 i ! ∣ ( a 1 + 1 ) ( a 1 + 1 ) ( a 1 + 2 ) ⋯ ∏ i = 1 n ( a 1 + i ) ( a 2 + 1 ) ( a 2 + 1 ) ( a 2 + 2 ) ⋯ ∏ i = 1 n ( a 2 + i ) ⋮ ⋮ ⋯ ⋮ ( a n + 1 ) ( a n + 1 ) ( a n + 2 ) ⋯ ∏ i = 1 n ( a n + i ) ∣ = ∏ i = 1 n 1 i ! ∣ ( a 1 + 1 ) ( a 2 + 1 ) ⋯ ( a n + 1 ) ( a 1 + 1 ) ( a 1 + 2 ) ( a 2 + 1 ) ( a 2 + 2 ) ⋯ ( a n + 1 ) ( a n + 2 ) ⋮ ⋮ ⋯ ⋮ ∏ i = 1 n ( a 1 + i ) ∏ i = 1 n ( a 2 + i ) ⋯ ∏ i = 1 n ( a n + i ) ∣ = ∏ i = 1 n 1 i ! ∣ ( a 1 + 1 ) ( a 2 + 1 ) ⋯ ( a n + 1 ) ( a 1 + 1 ) 2 ( a 2 + 1 ) 2 ⋯ ( a n + 1 ) 2 ⋮ ⋮ ⋯ ⋮ ( a 1 + 1 ) n ( a 2 + 1 ) n ⋯ ( a n + 1 ) n ∣ = ∏ i = 1 n a i + 1 i ! ∣ 1 1 ⋯ 1 a 1 + 1 a 2 + 1 ⋯ a n + 1 ⋮ ⋮ ⋯ ⋮ ( a 1 + 1 ) n − 1 ( a 2 + 1 ) n − 1 ⋯ ( a n + 1 ) n − 1 ∣ = ∏ i = 1 n a i + 1 i ! ∏ j = 1 i − 1 ( a i − a j ) \begin{aligned} |M| &= \left | \begin{matrix} \binom{a_1 + 1}{1} & \binom{a_1 + 2}{2} & \cdots & \binom{a_1 + n}{n} \\ \binom{a_2 + 1}{1} & \binom{a_2 + 2}{2} & \cdots & \binom{a_2 + n}{n} \\ \vdots & \vdots & \cdots & \vdots \\ \binom{a_n + 1}{1} & \binom{a_n + 2}{2} & \cdots & \binom{a_n + n}{n} \end{matrix} \right| \\ &= \left | \begin{matrix} \frac{(a_1 + 1)!}{1!a_1!} & \frac{(a_1 + 2)!}{2!a_1!} & \cdots & \frac{(a_1 + n)!}{n!a_1!} \\ \frac{(a_2 + 1)!}{1!a_2!} & \frac{(a_2 + 2)!}{2!a_2!} & \cdots & \frac{(a_2 + n)!}{n!a_2!} \\ \vdots & \vdots & \cdots & \vdots \\ \frac{(a_n + 1)!}{1!a_n!} & \frac{(a_n + 2)!}{2!a_n!} & \cdots & \frac{(a_n + n)!}{n!a_n!} \\ \end{matrix} \right| \\ &= \prod_{i=1}^n{\frac{1}{i!}}\left | \begin{matrix} \frac{(a_1 + 1)!}{a_1!} & \frac{(a_1 + 2)!}{a_1!} & \cdots & \frac{(a_1 + n)!}{a_1!} \\ \frac{(a_2 + 1)!}{a_2!} & \frac{(a_2 + 2)!}{a_2!} & \cdots & \frac{(a_2 + n)!}{a_2!} \\ \vdots & \vdots & \cdots & \vdots \\ \frac{(a_n + 1)!}{a_n!} & \frac{(a_n + 2)!}{a_n!} & \cdots & \frac{(a_n + n)!}{a_n!} \\ \end{matrix} \right| \\ &= \prod_{i=1}^n{\frac{1}{i!}}\left | \begin{matrix} (a_1 + 1) & (a_1 + 1)(a_1 + 2) & \cdots & \prod_{i=1}^n{(a_1 + i)} \\ (a_2 + 1) & (a_2 + 1)(a_2 + 2) & \cdots & \prod_{i=1}^n{(a_2 + i)} \\ \vdots & \vdots & \cdots & \vdots \\ (a_n + 1) & (a_n + 1)(a_n + 2) & \cdots & \prod_{i=1}^n{(a_n + i)} \\ \end{matrix} \right| \\ &= \prod_{i=1}^n{\frac{1}{i!}}\left | \begin{matrix} (a_1 + 1) & (a_2 + 1) & \cdots & (a_n + 1) \\ (a_1 + 1)(a_1 + 2) & (a_2 + 1)(a_2 + 2) & \cdots & (a_n + 1)(a_n + 2) \\ \vdots & \vdots & \cdots & \vdots \\ \prod_{i=1}^n{(a_1 + i)} & \prod_{i=1}^n{(a_2 + i)} & \cdots & \prod_{i=1}^n{(a_n + i)} \\ \end{matrix} \right| \\ &= \prod_{i=1}^n{\frac{1}{i!}}\left | \begin{matrix} (a_1 + 1) & (a_2 + 1) & \cdots & (a_n + 1) \\ (a_1 + 1)^2 & (a_2 + 1)^2 & \cdots & (a_n + 1)^2 \\ \vdots & \vdots & \cdots & \vdots \\ (a_1 + 1)^n & (a_2 + 1)^n & \cdots & (a_n + 1)^n \\ \end{matrix} \right| \\ &= \prod_{i=1}^n{\frac{a_i + 1}{i!}}\left | \begin{matrix} 1 & 1 & \cdots & 1 \\ a_1 + 1 & a_2 + 1 & \cdots & a_n + 1 \\ \vdots & \vdots & \cdots & \vdots \\ (a_1 + 1)^{n-1} & (a_2 + 1)^{n-1} & \cdots & (a_n + 1)^{n-1} \\ \end{matrix} \right| \\ &= \prod_{i=1}^n{\frac{a_i + 1}{i!} \prod_{j=1}^{i-1}{(a_i - a_j)}} \end{aligned} M=(1a1+1)(1a2+1)(1an+1)(2a1+2)(2a2+2)(2an+2)(na1+n)(na2+n)(nan+n)=1!a1!(a1+1)!1!a2!(a2+1)!1!an!(an+1)!2!a1!(a1+2)!2!a2!(a2+2)!2!an!(an+2)!n!a1!(a1+n)!n!a2!(a2+n)!n!an!(an+n)!=i=1ni!1a1!(a1+1)!a2!(a2+1)!an!(an+1)!a1!(a1+2)!a2!(a2+2)!an!(an+2)!a1!(a1+n)!a2!(a2+n)!an!(an+n)!=i=1ni!1(a1+1)(a2+1)(an+1)(a1+1)a1+2)(a2+1)a2+2)(an+1)an+2)i=1n(a1+i)i=1n(a2+i)i=1n(an+i)=i=1ni!1(a1+1)(a1+1)a1+2)i=1n(a1+i)(a2+1)(a2+1)a2+2)i=1n(a2+i)(an+1)(an+1)an+2)i=1n(an+i)=i=1ni!1(a1+1)(a1+1)2(a1+1)n(a2+1)(a2+1)2(a2+1)n(an+1)(an+1)2(an+1)n=i=1ni!ai+11a1+1(a1+1)n11a2+1(a2+1)n11an+1(an+1)n1=i=1ni!ai+1j=1i1(aiaj)
至此计算 ∣ M ∣ |M| M O ( n 2 ) O(n^2) O(n2) 的,依然不可接受,考虑如何快速计算 ∏ j = 1 i − 1 a i − a j \begin{aligned} \prod_{j=1}^{i-1}{a_i - a_j} \end{aligned} j=1i1aiaj.

f ( x ) = ∑ i = 1 n x a i \begin{aligned} f(x) = \sum_{i=1}^{n}{x^{a_i}}\end{aligned} f(x)=i=1nxai g ( x ) = ∑ i = 1 n x − a i \begin{aligned}g(x) = \sum_{i=1}^{n}{x^{-a_i}} \end{aligned} g(x)=i=1nxai.

用 NTT 处理出 h = f ∗ g = ∑ c i x i \begin{aligned} h = f*g = \sum{c_ix^i} \end{aligned} h=fg=cixi.

( a i − a j ) (a_i - a_j) (aiaj) 计算贡献,则 ∏ i = 1 n ∏ j = 1 i − 1 ( a i − a j ) = ∏ i > 0 i c i \begin{aligned} \prod_{i=1}^n{\prod_{j=1}^{i-1}(a_i - a_j)} = \prod_{i > 0}{i^{c_i}} \end{aligned} i=1nj=1i1(aiaj)=i>0ici.

因此答案为 ∏ i = 1 n a i + 1 i ! × ∏ i > 0 i c i \begin{aligned} \prod_{i=1}^{n}{\frac{a_i + 1}{i!}} \times \prod_{i>0}{i^{c_i}} \end{aligned} i=1ni!ai+1×i>0ici.

四、代码实现

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

template <typename T>
inline void read(T& x)
{
    x = 0; int f = 1; char ch = getchar();
    while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
    while(isdigit(ch))  {x = x * 10 + ch - '0', ch = getchar();}
    x *= f;
}

template <typename T>
void print(T x)
{
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) print(x / 10);
    putchar(x % 10 + '0');
}

template <typename T>
void print(T x, char ch)
{
    print(x), putchar(ch);
}

typedef double db;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;

template <ui M_> struct ModInt {
    static constexpr ui M = M_;
    ui x;
    constexpr ModInt(): x(0U){}
    constexpr ModInt(ui x_): x(x_ % M){}
    constexpr ModInt(ull x_): x(x_ % M){}
    constexpr ModInt(int x_): x(((x_ %= static_cast<int>(M)) < 0) ? (x_ + static_cast<int>(M)) : x_){}
    constexpr ModInt(ll x_): x(((x_ %= static_cast<ll>(M)) < 0) ? (x_ + static_cast<ll>(M)) : x_){}
    ModInt &operator+=(const ModInt &a) {x = ((x += a.x) >= M) ? (x - M) : x; return *this;}
    ModInt &operator-=(const ModInt &a) {x = ((x -= a.x) >= M) ? (x + M) : x; return *this;}
    ModInt &operator*=(const ModInt &a) {x = (static_cast<ull>(x) * a.x) % M; return *this;}
    ModInt &operator/=(const ModInt &a) {return (*this *= a.inv());}
    ModInt pow(ll e) const {
        if(e < 0) return inv().pow(-e);
        ModInt a = *this, b = 1U; for(; e; e >>= 1) {if(e & 1) b *= a; a *= a;} return b;
    }
    ModInt inv() const {
        ui a = M, b = x; int y = 0, z = 1;
        while(b) {const ui q = a / b; const ui c = a - q * b; a = b; b = c; const int w = y - static_cast<int>(q) * z; y = z; z = w;}
        return ModInt(y);
    }
    ModInt operator+() const {return *this;}
    ModInt operator-() const {ModInt a; a.x = x ? (M - x) : 0U; return a;}
    ModInt operator+(const ModInt &a) const {return (ModInt(*this) += a);}
    ModInt operator-(const ModInt &a) const {return (ModInt(*this) -= a);}
    ModInt operator*(const ModInt &a) const {return (ModInt(*this) *= a);}
    ModInt operator/(const ModInt &a) const {return (ModInt(*this) /= a);}
    template <class T> friend ModInt operator+(T a, const ModInt &b) {return (ModInt(a) += b);}
    template <class T> friend ModInt operator-(T a, const ModInt &b) {return (ModInt(a) -= b);}
    template <class T> friend ModInt operator*(T a, const ModInt &b) {return (ModInt(a) *= b);}
    template <class T> friend ModInt operator/(T a, const ModInt &b) {return (ModInt(a) /= b);}
    explicit operator bool() const {return x;}
    bool operator==(const ModInt &a) const {return (x == a.x);}
    bool operator!=(const ModInt &a) const {return (x != a.x);}
    friend std::ostream &operator<<(std::ostream &os, const ModInt &a) {return os << a.x;}
};

constexpr unsigned MO = 998244353U;
constexpr unsigned MO2 = 2U * MO;
constexpr int FFT_MAX = 23;
using Mint = ModInt<MO>;
constexpr Mint FFT_ROOTS[FFT_MAX + 1] = {1U, 998244352U, 911660635U, 372528824U, 929031873U, 452798380U, 922799308U, 781712469U, 476477967U, 166035806U, 258648936U, 584193783U, 63912897U, 350007156U, 666702199U, 968855178U, 629671588U, 24514907U, 996173970U, 363395222U, 565042129U, 733596141U, 267099868U, 15311432U};
constexpr Mint INV_FFT_ROOTS[FFT_MAX + 1] = {1U, 998244352U, 86583718U, 509520358U, 337190230U, 87557064U, 609441965U, 135236158U, 304459705U, 685443576U, 381598368U, 335559352U, 129292727U, 358024708U, 814576206U, 708402881U, 283043518U, 3707709U, 121392023U, 704923114U, 950391366U, 428961804U, 382752275U, 469870224U};
constexpr Mint FFT_RATIOS[FFT_MAX] = {911660635U, 509520358U, 369330050U, 332049552U, 983190778U, 123842337U, 238493703U, 975955924U, 603855026U, 856644456U, 131300601U, 842657263U, 730768835U, 942482514U, 806263778U, 151565301U, 510815449U, 503497456U, 743006876U, 741047443U, 56250497U, 867605899U};
constexpr Mint INV_FFT_RATIOS[FFT_MAX] = {86583718U, 372528824U, 373294451U, 645684063U, 112220581U, 692852209U, 155456985U, 797128860U, 90816748U, 860285882U, 927414960U, 354738543U, 109331171U, 293255632U, 535113200U, 308540755U, 121186627U, 608385704U, 438932459U, 359477183U, 824071951U, 103369235U};

void fft(Mint *as, int n) {
    int m = n;
    if(m >>= 1) {
        for(int i = 0; i < m; ++i) {
            const ui x = as[i + m].x;
            as[i + m].x = as[i].x + MO - x;
            as[i].x += x;
        }
    }
    if(m >>= 1) {
        Mint prod = 1U;
        for(int h = 0, i0 = 0; i0 < n; i0 += (m<<1)) {
            for(int i = i0; i < i0 + m; ++i) {
                const ui x = (prod * as[i + m]).x;
                as[i + m].x = as[i].x + MO - x;
                as[i].x += x;
            }
            prod *= FFT_RATIOS[__builtin_ctz(++h)];
        }
    }
    while(m) {
        if(m >>= 1) {
            Mint prod = 1U;
            for(int h = 0, i0 = 0; i0 < n; i0 += (m<<1)) {
                for(int i = i0; i < i0 + m; ++i) {
                    const ui x = (prod * as[i + m]).x;
                    as[i + m].x = as[i].x + MO - x;
                    as[i].x += x;
                }
                prod *= FFT_RATIOS[__builtin_ctz(++h)];
            }
        }
        if(m >>= 1) {
            Mint prod = 1U;
            for(int h = 0, i0 = 0; i0 < n; i0 += (m<<1)) {
                for(int i = i0; i < i0 + m; ++i) {
                    const ui x = (prod * as[i + m]).x;
                    as[i].x = (as[i].x >= MO2) ? (as[i].x - MO2) : as[i].x;
                    as[i + m].x = as[i].x + MO - x;
                    as[i].x += x;
                }
                prod *= FFT_RATIOS[__builtin_ctz(++h)];
            }
        }
    }
    for(int i = 0; i < n; ++i) {
        as[i].x = (as[i].x >= MO2) ? (as[i].x - MO2) : as[i].x;
        as[i].x = (as[i].x >= MO) ? (as[i].x - MO) : as[i].x;
    }
}

void invFft(Mint *as, int n) {
    int m = 1;
    if(m < n>>1) {
        Mint prod = 1U;
        for(int h = 0, i0 = 0; i0 < n; i0 += (m<<1)) {
            for(int i = i0; i < i0 + m; ++i) {
                const ull y = as[i].x + MO - as[i + m].x;
                as[i].x += as[i + m].x;
                as[i + m].x = (prod.x * y) % MO;
            }
            prod *= INV_FFT_RATIOS[__builtin_ctz(++h)];
        }
        m <<= 1;
    }
    for(; m < n>>1; m <<= 1) {
        Mint prod = 1U;
        for(int h = 0, i0 = 0; i0 < n; i0 += (m<<1)) {
            for(int i = i0; i < i0 + (m>>1); ++i) {
                const ull y = as[i].x + MO2 - as[i + m].x;
                as[i].x += as[i + m].x;
                as[i].x = (as[i].x >= MO2) ? (as[i].x - MO2) : as[i].x;
                as[i + m].x = (prod.x * y) % MO;
            }
            for(int i = i0 + (m>>1); i < i0 + m; ++i) {
                const ull y = as[i].x + MO - as[i + m].x;
                as[i].x += as[i + m].x;
                as[i + m].x = (prod.x * y) % MO;
            }
            prod *= INV_FFT_RATIOS[__builtin_ctz(++h)];
        }
    }
    if(m < n) {
        for(int i = 0; i < m; ++i) {
            const ui y = as[i].x + MO2 - as[i + m].x;
            as[i].x += as[i + m].x;
            as[i + m].x = y;
        }
    }
    const Mint invN = Mint(n).inv();
    for(int i = 0; i < n; ++i) {
        as[i] *= invN;
    }
}

void fft(vector<Mint> &as) {
    fft(as.data(), as.size());
}

void invFft(vector<Mint> &as) {
    invFft(as.data(), as.size());
}

vector<Mint> convolve(vector<Mint>& as, vector<Mint>& bs) {
    if(as.empty() || bs.empty()) return {};
    const int len = as.size() + bs.size() - 1;
    int n = 1;
    for(; n < len; n <<= 1) {}
    as.resize(n); fft(as);
    bs.resize(n); fft(bs);
    for(int i = 0; i < n; ++i) as[i] *= bs[i];
    invFft(as);
    as.resize(len);
    return as;
}

const int M = (int)5e5;
const int N = (int)1e5;
const int S = (int)1e6;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)998244353;

int fac[M + 5], inv[M + 5], invfac[M + 5];

void init()
{
    fac[0] = fac[1] = inv[1] = invfac[0] = invfac[1] = 1;
    for(int i = 2; i <= M; ++i)
    {
        fac[i] = 1ll * fac[i - 1] * i % mod;
        inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
        invfac[i] = 1ll * invfac[i - 1] * inv[i] % mod;
    }
}

vector<Mint> as, bs;

ll quick(ll a, ll b, ll p = mod)
{
    ll s = 1;
    while(b)
    {
        if(b & 1) s = s * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return s % p;
}

void work()
{
    int n; read(n);
    int ans = 1;
    as.resize(S + 1), bs.resize(S + 1);
    for(int i = 1, a; i <= n; ++i)
    {
        read(a);
        as[a] = 1, bs[S - a] = 1;
        ans = 1ll * ans * (a + 1) % mod * invfac[i] % mod;
    }
    convolve(as, bs);
    int k = as.size();
    for(int i = S + 1; i < k; ++i) if(as[i].x) ans = 1ll * ans * quick(i - S, as[i].x) % mod;
    print(ans, '\n');
}

int main()
{
//    ios::sync_with_stdio(0);
//    cin.tie(0); cout.tie(0);
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    int T; read(T);
//    for(int ca = 1; ca <= T; ++ca)
//    {
//        work();
//    }
    init();
    work();
//    cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值