Pluses everywhere
题意:
给你一个
n
n
n位数
a
a
a,让你在这些数之间添加
k
k
k个
+
+
+号,然后得到一个合法表达式,求所有可能的不同的合法表达式之和,答案对
1
0
9
+
7
10^9 + 7
109+7取模。
分析:
题意很简单,但要怎么去想呢?这可以说是一道计数dp的题吧。
我们可以单独的考虑每一位
a
i
a_i
ai的贡献,首先可以肯定的是,一共有
n
−
1
n - 1
n−1个位置可以放
+
+
+号,然后我们根据离
a
i
a_i
ai最近且在
a
i
a_i
ai之后的
+
+
+号位置进行讨论:
- 最近的一个加号在 a i a_i ai后面,也就是说 a i a_i ai作为个位,那么剩下的 k − 1 k - 1 k−1个加号可以有 n − 2 n - 2 n−2个位置填,其贡献为: a i × C n − 2 k − 1 a_i \times C_{n - 2}^{k - 1} ai×Cn−2k−1
- 最近的一个加号在
a
i
+
1
a_{i + 1}
ai+1后面,也就是说
a
i
+
1
a_{i + 1}
ai+1作为十位,那么剩下的
k
−
1
k - 1
k−1个加号可以有
n
−
3
n - 3
n−3个位置填,其贡献为:
a
i
×
10
×
C
n
−
3
k
−
1
a_i \times 10 \times C_{n - 3}^{k - 1}
ai×10×Cn−3k−1
… - 依次类推,最近的一个加号在 a i + n − k − 1 a_{i + n - k - 1} ai+n−k−1后面,那么剩下的 k − 1 k - 1 k−1个加号可以有 n − 2 − ( n − k − 1 ) = = k − 1 n - 2 - (n - k - 1) == k - 1 n−2−(n−k−1)==k−1个位置填,其贡献为: a i × 1 0 n − k − 1 × C k − 1 k − 1 a_i \times 10^{n - k - 1} \times C_{k - 1}^{k - 1} ai×10n−k−1×Ck−1k−1
- 然后假如我们距离 a i a_i ai最近的一个加号在 a n a_n an后面,因为 a n a_n an后面不能放加号,所以我们还剩下 k k k个加号, i − 1 i - 1 i−1个位置可以放,其贡献为: a i × 1 0 n − i × C i − 1 k a_i \times 10^{n - i} \times C_{i - 1}^{k} ai×10n−i×Ci−1k
那么我们的答案就是这些贡献总和。
化简下可以得到一个最终的公式:
∑
i
=
1
n
−
k
1
0
i
−
1
×
(
∑
j
=
1
n
−
i
a
j
×
C
n
−
1
−
i
k
−
1
+
a
n
−
i
+
1
×
C
n
−
i
k
)
\large \sum_{i = 1}^{n - k}10^{i - 1} \times (\sum_{j = 1}^{n - i}a_j \times C_{n - 1 - i}^{k - 1} + a_{n - i + 1} \times C_{n - i}^{k})
∑i=1n−k10i−1×(∑j=1n−iaj×Cn−1−ik−1+an−i+1×Cn−ik)
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
const int maxn = 1e5 + 5;
ll qpow(ll a, ll b, ll mod) {
ll res = 1;
while (b) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
ll fac[maxn], inv[maxn];
void init(int n) {
fac[0] = 1;
for (int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % MOD;
inv[n] = qpow(fac[n], MOD - 2, MOD);
for (int i = n - 1; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % MOD;
}
ll C(int n, int m) {
if (n < m) return 0;
return fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}
int a[maxn];
ll dp[maxn], pre[maxn], res, cur = 1ll;
int main() {
int n, k;
char ch;
scanf("%d%d", &n, &k);
getchar();
for (int i = 1; i <= n; i++) {
scanf("%c", &ch);
a[i] = ch - '0';
pre[i] = pre[i - 1] + a[i];
}
init(n);
for (int i = 1; i <= n - k; i++) {
res = (res + cur * (pre[n - i] * C(n - i - 1, k - 1) % MOD +
a[n - i + 1] * C(n - i, k) % MOD) % MOD) % MOD;
cout << pre[n-i]*C(n-i-1,k-1) << " " << a[n-i+1]*C(n-i,k) << endl;
cur = cur * 10 % MOD;
}
printf("%lld\n", res);
return 0;
}