题解 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;
}
posted @ 2020-12-22 20:17  David_H_Devs  阅读(100)  评论(0)    收藏  举报