题解 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;
}

浙公网安备 33010602011771号