[NOI2020统一省选 A] 组合数问题 (推式子)

题意

给定四个整数 n , x , p , m n,x,p,m n,x,p,m,求
∑ i = 0 n f ( i ) × x i × ( n i ) \sum_{i=0}^{n}f(i)\times x^i\times \binom{n}{i} i=0nf(i)×xi×(in)
p p p 取模,其中 f ( x ) = a 0 + a 1 x + a 2 x 2 + ⋯ + a m x m f(x) = a_0 + a_1x + a_2x ^ 2 + \cdots + a_mx ^ m f(x)=a0+a1x+a2x2++amxm

1 ≤ n , x , p ≤ 1 0 9 , 0 ≤ a i ≤ 1 0 9 , 0 ≤ m ≤ min ⁡ ( n , 1 0 3 ) 1 \le n,x,p \le 10 ^ 9, 0 \le a_i \le 10 ^ 9, 0 \le m \le \min(n, 10 ^ 3) 1n,x,p109,0ai109,0mmin(n,103)

分析:

首先把 f ( i ) f(i) f(i) 带入原式
∑ i = 0 n x i × ( n i ) ∑ j = 0 m a j × i j \sum_{i=0}^{n} x^i\times \binom{n}{i} \sum_{j = 0} ^ {m} a_j \times i ^ {j} i=0nxi×(in)j=0maj×ij
看到 i j i ^ j ij,故想到展开 i j = ∑ k = 0 j { j k } i k ‾ i ^ j = \sum\limits_{k = 0} ^ {j} {j \brace k} i ^ {\underline k} ij=k=0j{kj}ik

∑ i = 0 n x i × ( n i ) ∑ j = 0 m a j ∑ k = 0 j { j k } × i ! ( i − k ) ! \sum_{i=0}^{n} x^i\times \binom{n}{i} \sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times \frac{i!}{(i - k)!} i=0nxi×(in)j=0majk=0j{kj}×(ik)!i!

把前面的 ( n i ) \dbinom{n}{i} (in) 放到最后面化简

∑ i = 0 n x i ∑ j = 0 m a j ∑ k = 0 j { j k } × n ! i ! × ( n − i ) ! × i ! ( i − k ) ! = ∑ i = 0 n x i ∑ j = 0 m a j ∑ k = 0 j { j k } × n ! ( n − i ) ! × ( i − k ) ! \sum_{i=0}^{n} x^i \sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times\dfrac{n!}{i! \times (n - i)!} \times \frac{i!}{(i - k)!} \\\\ = \sum_{i=0}^{n} x^i \sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times\dfrac{n!}{(n - i)! \times (i - k)!} i=0nxij=0majk=0j{kj}×i!×(ni)!n!×(ik)!i!=i=0nxij=0majk=0j{kj}×(ni)!×(ik)!n!

考虑凑组合数 ( n − k n − i ) = ( n − k ) ! ( n − i ) ! × ( i − k ) ! \dbinom{n - k}{n - i} = \dfrac{(n - k)!}{(n - i)! \times (i - k)!} (nink)=(ni)!×(ik)!(nk)!,所以分式上下同乘 ( n − k ) ! (n - k)! (nk)!,即
∑ i = 0 n x i ∑ j = 0 m a j ∑ k = 0 j { j k } × ( n − k n − i ) × n k ‾ \sum_{i=0}^{n} x^i \sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times \binom{n - k}{n - i} \times n ^ {\underline k} i=0nxij=0majk=0j{kj}×(nink)×nk
交换求和次序,将 i i i 放到最后求和

∑ j = 0 m a j ∑ k = 0 j { j k } × n k ‾ ∑ i = 0 n x i × ( n − k n − i ) = ∑ j = 0 m a j ∑ k = 0 j { j k } × n k ‾ ∑ i = 0 n x i × ( n − k i − k ) = ∑ j = 0 m a j ∑ k = 0 j { j k } × n k ‾ ∑ i = k n x i × ( n − k i − k ) \sum_{j = 0} ^ {m} a_{j} \sum_{k = 0} ^ {j} {j \brace k} \times n ^ {\underline k} \sum_{i=0}^{n} x^i \times \binom{n - k}{n - i} \\\\ = \sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times n ^ {\underline k} \sum_{i=0}^{n} x^i \times \binom{n - k}{i - k} \\\\ = \sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times n ^ {\underline k} \sum_{i=k}^{n} x^i \times \binom{n - k}{i - k} j=0majk=0j{kj}×nki=0nxi×(nink)=j=0majk=0j{kj}×nki=0nxi×(iknk)=j=0majk=0j{kj}×nki=knxi×(iknk)

做变换 ( i − k ) → i (i - k) \rightarrow i (ik)i

∑ j = 0 m a j ∑ k = 0 j { j k } × n k ‾ ∑ i = 0 n − k x i + k × ( n − k i ) = ∑ j = 0 m a j ∑ k = 0 j { j k } × n k ‾ × x k ∑ i = 0 n − k x i × ( n − k i ) \sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times n ^ {\underline k} \sum_{i=0}^{n - k} x^{i + k} \times \binom{n - k}{i} \\\\ = \sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times n ^ {\underline k} \times x ^ {k} \sum_{i=0}^{n - k} x^{i} \times \binom{n - k}{i} j=0majk=0j{kj}×nki=0nkxi+k×(ink)=j=0majk=0j{kj}×nk×xki=0nkxi×(ink)

考虑二项式展开 ( a + b ) n = ∑ i = 0 n ( n i ) a i b n − i (a + b) ^ n = \sum\limits_{i = 0} ^ {n} \dbinom{n}{i} a ^ {i} b ^ {n - i} (a+b)n=i=0n(in)aibni,所以 ∑ i = 0 n − k x i × ( n − k i ) = ( 1 + x ) n − k \sum\limits_{i=0}^{n - k} x^{i} \times \dbinom{n - k}{i} = (1 + x) ^ {n - k} i=0nkxi×(ink)=(1+x)nk,故式子变为
∑ j = 0 m a j ∑ k = 0 j { j k } × n k ‾ × x k × ( 1 + x ) n − k \sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times n ^ {\underline k} \times x ^ {k} \times (1 + x) ^ {n - k} j=0majk=0j{kj}×nk×xk×(1+x)nk
这样式子就变为 O ( m 2 ) O(m ^ 2) O(m2) 了,第二类斯特林数可以预处理,下降幂可以线性维护。

代码:

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
constexpr int N = 1e3;
int mod;
int norm(int x) {
    if (x < 0) {
        x += mod;
    }
    if (x >= mod) {
        x -= mod;
    }
    return x;
}
template<class T>
T power(T a, int b) {
    T res = 1;
    for (; b; b /= 2, a *= a) {
        if (b % 2) {
            res *= a;
        }
    }
    return res;
}
struct Z {
    int x;
    Z(int x = 0) : x(norm(x)) {}
    int val() const {
        return x;
    }
    Z operator-() const {
        return Z(norm(mod - x));
    }
    Z inv() const {
        assert(x != 0);
        return power(*this, mod - 2);
    }
    Z &operator*=(const Z &rhs) {
        x = i64(x) * rhs.x % mod;
        return *this;
    }
    Z &operator+=(const Z &rhs) {
        x = norm(x + rhs.x);
        return *this;
    }
    Z &operator-=(const Z &rhs) {
        x = norm(x - rhs.x);
        return *this;
    }
    Z &operator/=(const Z &rhs) {
        return *this *= rhs.inv();
    }
    friend Z operator*(const Z &lhs, const Z &rhs) {
        Z res = lhs;
        res *= rhs;
        return res;
    }
    friend Z operator+(const Z &lhs, const Z &rhs) {
        Z res = lhs;
        res += rhs;
        return res;
    }
    friend Z operator-(const Z &lhs, const Z &rhs) {
        Z res = lhs;
        res -= rhs;
        return res;
    }
    friend Z operator/(const Z &lhs, const Z &rhs) {
        Z res = lhs;
        res /= rhs;
        return res;
    }
    friend istream &operator>>(istream &is, Z &a) {
        i64 v;
        is >> v;
        a = Z(v);
        return is;
    }
    friend ostream &operator<<(ostream &os, const Z &a) {
        return os << a.val();
    }
};
vector<vector<Z>> stirling(N + 1, vector<Z>(N + 1));
void init() {
    stirling[0][0] = 1;
    for (int i = 1; i <= N; i ++) {
        for (int j = 1; j <= i; j ++) {
            stirling[i][j] = stirling[i - 1][j - 1] + j * stirling[i - 1][j];
        }
    }
}
signed main() {
    cin.tie(0) -> sync_with_stdio(0);
    int n, x, m;
    cin >> n >> x >> mod >> m;
    init();
    vector<Z> a(m + 1);
    for (int i = 0; i <= m; i ++) {
        cin >> a[i];
    }
    Z res;
    for (int j = 0; j <= m; j ++) {
        Z sum = 1;
        for (int k = 0, cnt = n; k <= j; k ++, cnt --) {
            res += a[j] * stirling[j][k] * power(Z(x), k) * sum * power(Z(1 + x), n - k);
            sum *= cnt;
        }
    }
    cout << res << "\n";
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值