題解 P5148 【大迴圈】
阿新 • • 發佈:2020-12-22
題意簡述
給定 \(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; }