[NOI2020统一省选 A] 组合数问题 (推式子)
题意
给定四个整数
n
,
x
,
p
,
m
n,x,p,m
n,x,p,m,求
∑
i
=
0
n
f
(
i
)
×
x
i
×
(
n
i
)
\sum_{i=0}^{n}f(i)\times x^i\times \binom{n}{i}
i=0∑nf(i)×xi×(in)
对
p
p
p 取模,其中
f
(
x
)
=
a
0
+
a
1
x
+
a
2
x
2
+
⋯
+
a
m
x
m
f(x) = a_0 + a_1x + a_2x ^ 2 + \cdots + a_mx ^ m
f(x)=a0+a1x+a2x2+⋯+amxm
1 ≤ n , x , p ≤ 1 0 9 , 0 ≤ a i ≤ 1 0 9 , 0 ≤ m ≤ min ( n , 1 0 3 ) 1 \le n,x,p \le 10 ^ 9, 0 \le a_i \le 10 ^ 9, 0 \le m \le \min(n, 10 ^ 3) 1≤n,x,p≤109,0≤ai≤109,0≤m≤min(n,103)
分析:
首先把
f
(
i
)
f(i)
f(i) 带入原式
∑
i
=
0
n
x
i
×
(
n
i
)
∑
j
=
0
m
a
j
×
i
j
\sum_{i=0}^{n} x^i\times \binom{n}{i} \sum_{j = 0} ^ {m} a_j \times i ^ {j}
i=0∑nxi×(in)j=0∑maj×ij
看到
i
j
i ^ j
ij,故想到展开
i
j
=
∑
k
=
0
j
{
j
k
}
i
k
‾
i ^ j = \sum\limits_{k = 0} ^ {j} {j \brace k} i ^ {\underline k}
ij=k=0∑j{kj}ik
∑ i = 0 n x i × ( n i ) ∑ j = 0 m a j ∑ k = 0 j { j k } × i ! ( i − k ) ! \sum_{i=0}^{n} x^i\times \binom{n}{i} \sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times \frac{i!}{(i - k)!} i=0∑nxi×(in)j=0∑majk=0∑j{kj}×(i−k)!i!
把前面的 ( n i ) \dbinom{n}{i} (in) 放到最后面化简
∑ i = 0 n x i ∑ j = 0 m a j ∑ k = 0 j { j k } × n ! i ! × ( n − i ) ! × i ! ( i − k ) ! = ∑ i = 0 n x i ∑ j = 0 m a j ∑ k = 0 j { j k } × n ! ( n − i ) ! × ( i − k ) ! \sum_{i=0}^{n} x^i \sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times\dfrac{n!}{i! \times (n - i)!} \times \frac{i!}{(i - k)!} \\\\ = \sum_{i=0}^{n} x^i \sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times\dfrac{n!}{(n - i)! \times (i - k)!} i=0∑nxij=0∑majk=0∑j{kj}×i!×(n−i)!n!×(i−k)!i!=i=0∑nxij=0∑majk=0∑j{kj}×(n−i)!×(i−k)!n!
考虑凑组合数
(
n
−
k
n
−
i
)
=
(
n
−
k
)
!
(
n
−
i
)
!
×
(
i
−
k
)
!
\dbinom{n - k}{n - i} = \dfrac{(n - k)!}{(n - i)! \times (i - k)!}
(n−in−k)=(n−i)!×(i−k)!(n−k)!,所以分式上下同乘
(
n
−
k
)
!
(n - k)!
(n−k)!,即
∑
i
=
0
n
x
i
∑
j
=
0
m
a
j
∑
k
=
0
j
{
j
k
}
×
(
n
−
k
n
−
i
)
×
n
k
‾
\sum_{i=0}^{n} x^i \sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times \binom{n - k}{n - i} \times n ^ {\underline k}
i=0∑nxij=0∑majk=0∑j{kj}×(n−in−k)×nk
交换求和次序,将
i
i
i 放到最后求和
∑ j = 0 m a j ∑ k = 0 j { j k } × n k ‾ ∑ i = 0 n x i × ( n − k n − i ) = ∑ j = 0 m a j ∑ k = 0 j { j k } × n k ‾ ∑ i = 0 n x i × ( n − k i − k ) = ∑ j = 0 m a j ∑ k = 0 j { j k } × n k ‾ ∑ i = k n x i × ( n − k i − k ) \sum_{j = 0} ^ {m} a_{j} \sum_{k = 0} ^ {j} {j \brace k} \times n ^ {\underline k} \sum_{i=0}^{n} x^i \times \binom{n - k}{n - i} \\\\ = \sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times n ^ {\underline k} \sum_{i=0}^{n} x^i \times \binom{n - k}{i - k} \\\\ = \sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times n ^ {\underline k} \sum_{i=k}^{n} x^i \times \binom{n - k}{i - k} j=0∑majk=0∑j{kj}×nki=0∑nxi×(n−in−k)=j=0∑majk=0∑j{kj}×nki=0∑nxi×(i−kn−k)=j=0∑majk=0∑j{kj}×nki=k∑nxi×(i−kn−k)
做变换 ( i − k ) → i (i - k) \rightarrow i (i−k)→i
∑ j = 0 m a j ∑ k = 0 j { j k } × n k ‾ ∑ i = 0 n − k x i + k × ( n − k i ) = ∑ j = 0 m a j ∑ k = 0 j { j k } × n k ‾ × x k ∑ i = 0 n − k x i × ( n − k i ) \sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times n ^ {\underline k} \sum_{i=0}^{n - k} x^{i + k} \times \binom{n - k}{i} \\\\ = \sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times n ^ {\underline k} \times x ^ {k} \sum_{i=0}^{n - k} x^{i} \times \binom{n - k}{i} j=0∑majk=0∑j{kj}×nki=0∑n−kxi+k×(in−k)=j=0∑majk=0∑j{kj}×nk×xki=0∑n−kxi×(in−k)
考虑二项式展开
(
a
+
b
)
n
=
∑
i
=
0
n
(
n
i
)
a
i
b
n
−
i
(a + b) ^ n = \sum\limits_{i = 0} ^ {n} \dbinom{n}{i} a ^ {i} b ^ {n - i}
(a+b)n=i=0∑n(in)aibn−i,所以
∑
i
=
0
n
−
k
x
i
×
(
n
−
k
i
)
=
(
1
+
x
)
n
−
k
\sum\limits_{i=0}^{n - k} x^{i} \times \dbinom{n - k}{i} = (1 + x) ^ {n - k}
i=0∑n−kxi×(in−k)=(1+x)n−k,故式子变为
∑
j
=
0
m
a
j
∑
k
=
0
j
{
j
k
}
×
n
k
‾
×
x
k
×
(
1
+
x
)
n
−
k
\sum_{j = 0} ^ {m} a_j \sum_{k = 0} ^ {j} {j \brace k} \times n ^ {\underline k} \times x ^ {k} \times (1 + x) ^ {n - k}
j=0∑majk=0∑j{kj}×nk×xk×(1+x)n−k
这样式子就变为
O
(
m
2
)
O(m ^ 2)
O(m2) 了,第二类斯特林数可以预处理,下降幂可以线性维护。
代码:
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
constexpr int N = 1e3;
int mod;
int norm(int x) {
if (x < 0) {
x += mod;
}
if (x >= mod) {
x -= mod;
}
return x;
}
template<class T>
T power(T a, int b) {
T res = 1;
for (; b; b /= 2, a *= a) {
if (b % 2) {
res *= a;
}
}
return res;
}
struct Z {
int x;
Z(int x = 0) : x(norm(x)) {}
int val() const {
return x;
}
Z operator-() const {
return Z(norm(mod - x));
}
Z inv() const {
assert(x != 0);
return power(*this, mod - 2);
}
Z &operator*=(const Z &rhs) {
x = i64(x) * rhs.x % mod;
return *this;
}
Z &operator+=(const Z &rhs) {
x = norm(x + rhs.x);
return *this;
}
Z &operator-=(const Z &rhs) {
x = norm(x - rhs.x);
return *this;
}
Z &operator/=(const Z &rhs) {
return *this *= rhs.inv();
}
friend Z operator*(const Z &lhs, const Z &rhs) {
Z res = lhs;
res *= rhs;
return res;
}
friend Z operator+(const Z &lhs, const Z &rhs) {
Z res = lhs;
res += rhs;
return res;
}
friend Z operator-(const Z &lhs, const Z &rhs) {
Z res = lhs;
res -= rhs;
return res;
}
friend Z operator/(const Z &lhs, const Z &rhs) {
Z res = lhs;
res /= rhs;
return res;
}
friend istream &operator>>(istream &is, Z &a) {
i64 v;
is >> v;
a = Z(v);
return is;
}
friend ostream &operator<<(ostream &os, const Z &a) {
return os << a.val();
}
};
vector<vector<Z>> stirling(N + 1, vector<Z>(N + 1));
void init() {
stirling[0][0] = 1;
for (int i = 1; i <= N; i ++) {
for (int j = 1; j <= i; j ++) {
stirling[i][j] = stirling[i - 1][j - 1] + j * stirling[i - 1][j];
}
}
}
signed main() {
cin.tie(0) -> sync_with_stdio(0);
int n, x, m;
cin >> n >> x >> mod >> m;
init();
vector<Z> a(m + 1);
for (int i = 0; i <= m; i ++) {
cin >> a[i];
}
Z res;
for (int j = 0; j <= m; j ++) {
Z sum = 1;
for (int k = 0, cnt = n; k <= j; k ++, cnt --) {
res += a[j] * stirling[j][k] * power(Z(x), k) * sum * power(Z(1 + x), n - k);
sum *= cnt;
}
}
cout << res << "\n";
}