题意:给出一个n和一个K,n表示给出一个长度为n的只含数字的字符串,k表示要在这n个字符之间加上k个加号,并得到这个算式的最终值,求所有可能出现的划分的算式的总和。
思路:当第i个数是其中一个划分块的个位时,这时第i位数后面一定有一个“+”,也就是说,现在是有k-1个加号,需要填到n-2个空位中,方案数为C(k-1)(n-2)。((k-1)在上,为组合数)
故可以推出规律,对于每个数字,在最终方案里,充当一个划分块的第i位的次数是相同的,每个数字在充当一个划分块的第i位的次数为C(k-1)(n-i-1)。所以我们枚举i,计算所有数字在充当第i位所做的贡献,并不断累加即可。
而所有数字充当划分块的第i位为最终答案做出的贡献为sum[n-i] * 10^(i-1) *C(k-1)(n-i-1)。(sum为该数组的前缀和,因为可能充当一个划分块的第i位数的数字,只有从数组的后(i-1)个数字无法充当一个划分块的第i位)。最后注意一点,因为数据较大,所以在计算组合数的时候要用逆元计算,否则会出错。
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 100050;
#define mod 1000000007
typedef long long LL;
LL sum[N] , pow[N] , jc[N] , n , k;
char s[N];
LL extend_Euclid(LL a , LL b , LL &x , LL &y)
{
if(b == 0)
{
x = 1;
y = 0;
return a;
}
LL gcd = extend_Euclid(b , a % b , x , y);
LL tmp = x;
x = y;
y = tmp - (a / b) * y;
return gcd;
}
LL rev(LL a)
{
LL x,y;
extend_Euclid(a,mod,x,y);
return (x % mod + mod) % mod;
}
LL c(LL n , LL m)
{
return jc[n] * rev(jc[m]) % mod * rev(jc[n-m]) % mod;
}
int main()
{
cin >> n >> k;
scanf("%s", s + 1);
sum[0] = 0; jc[0] = 1 , pow[0] = 1;
for(LL i=1; i<=n; ++i)
sum[i] = (sum[i-1] + (s[i] - '0')) % mod;
if(k == 0)
{
LL ans = 0;
for(LL i=1; i<=n; ++i)
ans = (ans * 10 + s[i] - '0') % mod;
cout << ans << endl;
return 0;
}
for(LL i=1; i<=n; ++i)
jc[i] = (jc[i-1] * i) % mod;
for(LL i=1; i<=n; ++i)
pow[i] = (pow[i-1] * 10) % mod;
LL ans = 0;
for(LL i=1; i<=n-k; ++i)
{
ans = ((ans + sum[n-i] * pow[i-1] % mod * c(n-i-1 , k-1) % mod)) % mod;
ans = ((ans + (s[n-i+1] - '0') * pow[i-1] % mod * c(n-i , k) % mod)) % mod;
}
cout << ans << endl;
return 0;
}