洛谷 P1313 计算系数(数论,二项式定理)

题意

给定一个多项式 ( b y + a x ) k (by+ax)^k (by+ax)k,求多项式展开后 x n × y m x^n \times y^m xn×ym项的系数。这个系数可能很大,只需要求出对10007取模后的结果。 ( 0 ≤ a , b ≤ 1 0 6 , k ≤ 1000 ) (0≤a,b≤10^6, k \leq 1000) (0a,b106,k1000)

解题思路

这是一道很简单的数论题,但我感觉从里面学到了很多知识。首先就是二项式定理: ( x + y ) n = ∑ k = 0 n ( n k ) x n − k y k (x+y)^n = \sum\limits_{k=0}^n {n \choose k}x^{n-k}y^k (x+y)n=k=0n(kn)xnkyk。那么对于 x n × y m x^n \times y^m xn×ym项,只需要套公式就行了,答案为 ( k m ) × a m × b n {k \choose m}\times a^m \times b^n (mk)×am×bn

对于 ( k m ) {k \choose m} (mk),由于 k k k比较大,所以我们需要找一种高效的求组合数的方式。如果用杨辉三角感觉复杂度有点高( O ( k 2 ) O(k^2) O(k2))。但是有这样一个等式 ( k n ) = n − k + 1 k ( k − 1 n ) {k \choose n} = \frac{n - k +1}{k} {k - 1\choose n} (nk)=knk+1(nk1),如果我们从 ( 0 n ) {0 \choose n} (n0)开始从左到右推可以线性的求出组合数的值。

但是问题又来了,这个算法计算的时候需要除以 k k k,而除法在模运算下不成立。我们可以利用模运算的逆来解决这个问题。原公式变为 ( k n ) = ( n − k + 1 ) ( k − 1 n ) × k − 1 m o d    10007 {k \choose n} = (n - k +1){k - 1 \choose n} \times k^{-1} \mod 10007 (nk)=(nk+1)(nk1)×k1mod10007。因为模数是质数且大于 k k k,所以一定存在 k k k对于10007的逆元。求逆元的方法就多了,可以用快速幂法,也可以用扩展欧几里得,都是 O ( log ⁡ N ) O(\log N) O(logN)的复杂度。至于两个数的幂,可以使用快速幂轻松解决,然后乘起来就是答案了。

时间复杂度

O ( k log ⁡ k + log ⁡ n ) O(k\log k + \log n) O(klogk+logn)

代码

#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;

typedef long long ll;

const int INF = 2147483647;
const int INF2 = 0x3f3f3f3f;
const ll INF64 = 1e18;
const double INFD = 1e30;
const double EPS = 1e-6;
const double PI = 3.1415926;
const ll MOD = 1e9;

inline int read() {
    int X = 0, w = 0;
    char ch = 0;
    while (!isdigit(ch)) {
        w |= ch == '-';
        ch = getchar();
    }
    while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
    return w ? -X : X;
}

int quickM(ll x, ll p, int mod) {
    if (mod == 0) return 1;
    x %= mod;
    ll ans = 1;
    while (p) {
        if (p & 1) ans = ans * x % mod;
        x = x * x % mod;
        p >>= 1;
    }
    return ans;
}
int n, m;
int CASE;
const int MAXN = 1005;
ll S[MAXN];

int main() {
#ifdef LOCALLL
    freopen("in", "r", stdin);
    freopen("out", "w", stdout);
#endif
    int a, b, k;
    scanf("%d %d %d %d %d", &a, &b, &k, &n, &m);
    S[0] = 1;
    for (int i = 1; i <= k; i++) {
        S[i] = S[i - 1] * (k - i + 1);
        S[i] %= 10007;
        // 乘以逆元
        S[i] *= quickM(i, 10005, 10007);
        S[i] %= 10007;
    }
    int xn = S[n];
    int bb = quickM(b, m, 10007);
    int aa = quickM(a, n, 10007);
    ll ans = ((ll)xn * (ll)aa) % 10007;
    ans *= (ll)bb;
    ans %= 10007;
    printf("%lld\n", ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值