转载请注明出处,谢谢http://blog.csdn.net/bigtiao097?viewmode=contents
题意:
给定长度为n的数字串,在空隙添上k个+号(每个空隙最多添加一个),得到合法的加法式子,求上述得到的所有的式子的计算结果之和%1e9+7
(0≤k<n≤1e5)
思路:
考虑每个数字对答案的贡献值,总共有n-1个位置可以放加号。下文中左边指高位,右边指地位。
对于一个数字x来说,考虑以下情况:
- 考虑其作个位时,也就是其右边第一个空隙为加号,那么其余k-1个加号随便放在剩下的n-2个位置就好了,这时对答案的贡献为x;
- 其作十位数的时候,那么加号一定在其右边第二个空隙上,其余k-1个加号随便放在剩下的n-3个位置(除去x右边第一个空隙)就好了,这时对答案的贡献为10x
- 以此类推……
- 还有一种情况就是加号全部放在x的左边,这时对答案的贡献要根据x的位置来决定
但是问题来了,这样会使得复杂度成为
O(n2)
,显然不行
不过你会发现,上述过程有好多重复的工作,对于每个数考虑其右边第一个加号时过程几乎是一样的,只是考虑的次数不同,这样完全可以利用前面算出来的结果,求个前缀和,接着累加就行
最后的问题就是求组合数,提前处理好1~n的阶乘以及对1e9+7的逆元就好了
具体代码如下:
Result:Accepted Memory:5272 KB Time : 46ms
#include<bits/stdc++.h>
const int maxn = 1e5+5;
const int mod = 1e9+7;
using namespace std;
typedef long long ll;
ll fac[maxn];
ll inv[maxn];
ll sum[maxn];
char s[maxn];
ll a[maxn];
int n,k;
ll ans;
ll ten;
ll mod_pow(ll x,ll n)
{
x%=mod;
ll res = 1;
while(n>0)
{
if(n&1) res = res*x%mod;
x = x*x%mod;
n>>=1;
}
return res;
}
void init()
{
inv[0]=1;fac[0]=1;
for(int i=1;i<=n;i++)
{
fac[i] = (fac[i-1]*i)%mod;
inv[i] = mod_pow(fac[i],mod-2)%mod;
}
}
ll C(ll n,ll m)
{
if(n<0||m<0) return 0;
if(m>n) return 0;
return (((fac[n]*inv[m])%mod)*inv[n-m])%mod;
}
int main()
{
cin>>n>>k;
init();
scanf("%s",s+1);
for(int i=1;i<=n;i++)
a[i] = s[i]-'0';
ten = 1;
for(int i=n-1;i>=1;i--)
{
sum[i] = (sum[i+1]+ten*C(i-1,k-1))%mod;
//枚举右边第一个加号式,利用前面的计算结果
ten = (ten*10)%mod;
}
ten = 1;
for(int i=n;i>=k+1;i--)
{
sum[i] = (sum[i]+C(i-1,k)*ten)%mod;//考虑加号全部在左边
ten = (ten*10)%mod;
}
ans = 0;
for(int i=1;i<=n;i++)
ans = (ans+sum[i]*a[i])%mod;
cout<<ans<<endl;
}