题意:
有一个字符串,长度为n,都由数字组成。在其之间放k个加号,求出所有情况的和。
允许有前导0.
思路:
翻看了别人的题解,但还是看不懂。
只知道找出每个数字对最终结果的贡献,分个位、百位……
于是肉鸽尝试去找规律,结果还真找到了 = =。
例如
3 2
123
个位:1 1 1
4 2
1234
十位:1 1 1 0
个位:2 2 2 3
5 2
12345
百位:1 1 1 0 0
十位:2 2 2 3 0
个位:3 3 3 3 6
在第i个数字时,把前面的结果乘上10,然后再加上前i个数字的个位数对结果的贡献即可(具体看代码)。
在求组合数时,先处理出阶乘以及阶乘的逆元(拓展欧几里得求),从而求得组合数。
code:#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
const int MOD = 1e9+7;
typedef long long LL;
int n, k;
LL jc[N], ny[N];
LL c[N];
char ch[N];
LL exgcd(LL a, LL b, LL &x, LL &y) {
if(!b) {
x = 1;
y = 0;
return a;
}
LL ans = exgcd(b, a%b, y, x);
y -= a/b*x;
return ans;
}
LL solve() {
//jc:阶乘
jc[0] = 1;
for(int i = 1;i <= n; i++) {
jc[i] = jc[i-1]*i%MOD;
}
//ny:逆元
LL tmp, x, y;
for(int i = 0;i <= n; i++) {
tmp = exgcd(jc[i], MOD, x, y);
x = (x%MOD+MOD)%MOD;
ny[i] = x;
}
//C:都是以k作为要选取的个数。
for(int i = k;i <= n; i++) {
c[i] = jc[i]*ny[k]%MOD*ny[i-k]%MOD;
}
int sum = 0;
LL res = 0;
int len = strlen(ch);
for(int i = 1;i <= len; i++) {
sum += ch[i-1]-'0'; //sum保存数字的前缀和
if(i <= k) continue;
if(i == k+1) {
res = sum; //刚好能够放k个加号,因此res = sum;
}
else {//c(i-1,k)-c(i-2,k) = c(i-2, k-1),这是每个数字个位数的次数。
//当然对于当前第i个数字一定是个位,总的次数为c(i-1,k),所以后面要加上c[i-2]*(ch[i-1]-'0').
res = (res*10 + sum*(c[i-1]-c[i-2])%MOD+(ch[i-1]-'0')*c[i-2])%MOD;
}
}
return res;
}
int main() {
scanf("%d%d", &n, &k);
scanf("%s", ch);
printf("%I64d\n", solve());
return 0;
}