题意
给定一个多项式 ( 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) (0≤a,b≤106,k≤1000)
解题思路
这是一道很简单的数论题,但我感觉从里面学到了很多知识。首先就是二项式定理: ( 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=0∑n(kn)xn−kyk。那么对于 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)=kn−k+1(nk−1),如果我们从 ( 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)=(n−k+1)(nk−1)×k−1mod10007。因为模数是质数且大于 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;
}