题目概述
给定一个 \(n\) 位的十进制数,可以在数字之间加恰好 \(k\) 个 +
,得到一个式子,求每种方案的这个式子的和。
对 \(10^9+7\) 取模,数据范围:\(1\leq n\leq 10^5\)。
分析
有点意思。
不难想到设 \(f_{i,j}\) 表示前 \(i\) 个数填 \(j\) 个加号的方案和,转移是简单的,考虑在不在前面放 +
即可。
但是这不是本题的思路。
像这种求所有的全局的方案,一般考虑每一个位置对于总共答案的贡献是多少。
我们考虑从前往后的第 \(i\) 个位置,这个数填在当前分割出来的数的从前往后数第 \(j\) 位,显然 \(j\leq n - i + 1\)。
那么对于当前他的数值方面的贡献为 \(a_i\times 10^j\),那么它的方案为 \(C_{n-1-(j-1)-1}^{k-1}=C_{n-j-1}^{k-1}\)。
为什么是这个组合数?
\(n-1\) 是我们可选的位置,后面再 \(-1\) 是因为肯定所分割出来的数的后面有一个加号,再加上其长度为 \(j\),因此占有 \(j-1\) 个位置不能填 +
号。
那么这就引申出一个问题,当我的 \(j=n-i+1\) 时,那么我的后面是填不了 +
号的,因此此时方案为 \(C_{n-1-(j-1)}^{k}=C_{n-j}^k\)。
形式化地讲就是:
我们注意到后面的组合数跟 \(i\) 无关,考虑先枚举 \(j\),有:
显然后面那一个求和是可以前缀和优化的。
代码
时间复杂度 \(\mathcal{O}(n)\)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stdlib.h>
#include <vector>
#define int long long
#define N 100005
using namespace std;
const int mod = 1e9 + 7;
int jc[N],inv[N],sum[N],a[N];
int C(int a,int b) {if (a < 0 || b < 0 || a < b) return 0;return jc[a] * inv[b] % mod * inv[a - b] % mod;
}
signed main(){jc[0] = jc[1] = inv[0] = inv[1] = 1;for (int i = 2;i < N;i ++) jc[i] = jc[i - 1] * i % mod,inv[i] = (mod - mod / i) * inv[mod % i] % mod;for (int i = 2;i < N;i ++) inv[i] = inv[i - 1] * inv[i] % mod;int n,k;scanf("%lld%lld",&n,&k);for (int i = 1;i <= n;i ++) {char x;cin >> x;a[i] = x - '0';sum[i] = sum[i - 1] + a[i];}int ans = 0;for (int i = 1,t = 1;i <= n - k;i ++,t = t * 10 % mod) {ans = (ans + t * a[n - i + 1] % mod * C(n - i,k) % mod) % mod;ans = (ans + t * sum[n - i] % mod * C(n - i - 1,k - 1) % mod) % mod;}cout << ans;return 0;
}