1. 程式人生 > 實用技巧 >題解 P5148 【大迴圈】

題解 P5148 【大迴圈】

題意簡述

給定 \(q\)\(f(x) = \sum_{i=0}^{m}a_ix^i\)\(a_i\)\(i=0,1,2,3,\cdots,m\)),求出

\[\sum_{1\le a_1<a_2<\cdots<a_k\le n}{f(q)} \]

的值。

思路簡述

先對

\[\sum_{1\le a_1<a_2<\cdots<a_k\le n}{f(q)} \]

進行化簡:

\[\sum_{1\le a_1<a_2<\cdots<a_k\le n}f(q) =f(q)\sum_{1\le a_1<a_2<\cdots<a_m\le n}1 \]

\[\sum_{1\le a_1<a_2<\cdots<a_k\le n}1 \]

可以看做是從 \([1,n]\) 隨機選 \(k\) 個正整數的選法,有 \(\binom n k = \mathrm{C}_{n}^{k}\) 種選法。即原式為

\[f(q)\binom n k \]

對於 \(\binom n k\),一種比較好的計算方法如下:

\[\begin{aligned}\binom n k &= \frac{n!}{k!\cdot(n-k)!}\\&=\frac{\prod_{i=n-k+1}^{n}i}{k!}\end{aligned} \]

其中, \(\prod_{i=1}^{n}i=1\times 2\times 3\times \cdots \times n\)

。注意最後要乘法逆元算組合數。

現在對 \(f(x)\) 進行化簡。可以利用秦九韶演算法。例如:

\[\sum_{i=0}^{5}a_ix^i=x\left(x\left(x\left(x\left(a_5x+a_4\right)+a_3\right)+a_2\right)+a_1\right)+a_0 \]

即,把一個 \(n\) 次多項式轉化為 \(n\) 個一次多項式。具體的程式碼實現可見程式碼。

知道了這些,程式碼就很好寫了。

程式碼

#include <bits/stdc++.h>
typedef long long ll;
#define MOD ((ll)1e9 + 7)
using namespace std;
ll a[1000005];
ll n, m, k, q;
inline ll QuickPower(ll x, ll y, ll mod) { // 快速冪
    ll res = 1;
    while (y) {
        if (y & 1)
            res = res * x % mod;
        y >>= 1;
        x = (x * x) % mod;
    }
    return res;
}
inline int QJS(int x) {
    int ans = a[m] % MOD; // 賦初值是因為例子中的“a_0”,避免在最後做一次計算(懶)
    for (int i = m; i > 0; --i)
        ans = ((ll)1 * ans * x % MOD + a[i - 1]) % MOD;
    // 套用秦九韶公式
    return ans;
}
inline ll C(int n, int k) { // 組合數公式
    if (k * 2 > n)
        k = n - k;
    int QAQ = 1, QwQ = 1;
    for (int i = n; i >= n - k + 1; i--)
        QAQ = (ll)1 * QAQ * i % MOD;
    for (int i = 2; i <= k; i++)     // 從 1 開始節省時間
        QwQ = (ll)1 * QwQ * i % MOD; // 改成 *= 就玄學報錯
    return (ll)1 * QuickPower(QwQ, MOD - 2, MOD) * QAQ % MOD; // 乘法逆元
}
int main(void) {
    cin >> n >> m >> k >> q;
    for (int i = 0; i <= m; i++)
        cin >> a[i];
    cout << (ll)1 * QJS(q % MOD) * C(n, k) % MOD;
    return 0;
}