题目来源: CodeForces
基准时间限制:1 秒 空间限制:131072 KB 分值: 160 难度:6级算法题
题目描述:
现在要给一个长度为n数字串上面加上恰好k个加号,把所有可能的算术结果相加起来。
加号加到数字串中间之后要形成正确的算术表达式。规则是:没有两个加号连在一起,两个加号之间至少要有一位数字,加号不能加在开头,也不能加在结尾。比如数字串是10500,那么100500(加0个加号),1+00+500 或者 10050+0 这些放置的加号都是合法的,而100++500, +1+0+0+5+0+0 和100500+都是非法的。结果比较大,对 109+7 取余输出即可。
样例解释:
在第一个例子中 (1+08)+(10+8)=27。
在第二个例子中 1+0+8=9。
Input
单组测试数据。
第一行有两个整数n 和k (0≤k<n≤10^5)。
第二行包含n位数字。
Output
输出结果占一行。
Input示例
样例输入1
3 1
108
样例输入2
3 2
108
Output示例
样例输出1
27
样例输出1
9
题解:对于此类问题通常是讲n个数一位一位来看的每一个数可以作为各位数,前n-1个数可以作为十位数,前n-2位数可以作为百位数……末尾到首开始看每一位对总和的贡献=
倒数第一位:贡献了C(n-1,k)次个位数
倒数第二位:贡献了C(n-2,k-1)次个位数,C(n-2,k)次十位数
倒数第三位:贡献了C(n-2,k-1)次个位数,C(n-3,k-1)次十位数,C(n-3,k)次
倒数第四位:贡献了C(n-2,k-1)次个位数,C(n-3,k-1)次十位数,C(n-4,k-1)次百位数,C(n-4,k)次千位数
1.求答案,考虑每个数作为i位数(可为答案贡献10的i-1次方,个位i=1,十位i=2,...,最多n-k位):
那么它及后面 共i个数 之间不能有加号。
且只有前n-i+1个数可以作为i位,如果是an-i+1作为i位,那么后面都不能有加号,k个加号在a1到an-i+1之间,所以有C(n-i,k)次贡献(这么说怪怪的→_←),就是几种情况。
a1 a2 a3 ... an-i+1 ... an
如果是a1、a2、...an-i作为i位,比如a1,那就是ai和ai+1之间用掉一个加号,其它加号ai+1的后面n-1-i个间隔里。
a1 a2 a3 ... ai + ai+1 ... an
再比如a2,剩下k-1个加号在ai+2的后面及a1和a2之间 n-1-i个间隔里。
a1 a2 a3 ... ai+1 + ai+2 ... an
所以他们都做了C(n-i-1,k-1)次贡献。
于是就有ans=∑(i=1到n-k)[(a1+a2+...an-i)*C(n-i-1,k-1)+an-i+1*C(n-i,k)]%M。
s[i]为前缀和,ans=∑(i=1到n-k)[s[n-i]*C(n-i-1,k-1)+an-i+1*C(n-i,k)]%M。
2.组合数取模
由费马小定理,当a和p互质时:ap-1≡1 mod p 可得 a*ap-2≡1 mod p,ap-2和a互为逆元。
a/b mod p=a*b-1 mod p 也就是求除数的逆元。
C(a,b)=a!/[b!*(a-b)!]
所以先求出所有1到n的阶乘,和它的逆。
计算出了n!的逆元之后,再根据n!=n*(n-1)!,两边同时乘以INV(n),INV(n-1),化简得:INV(n-1)=n*INV(n),因此可以利用该公式递推求出其他的逆元
总结:做题中一定要有拆的思想,因为涉及到方案数,所以很有可能和组合数学有关,而且在不同的情况下每一位的状态是不同的,所以应该把状态分开来看。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define mod 1000000007
#define N 100005
using namespace std;
int n,k;
long long date[N],s[N];
char str[N];
long long re[N],inv[N];
long long pow(long long x)//快速幂
{
long long y=mod-2,ans=1;
while(y)
{
if(y&1) ans=ans*x%mod;
y>>=1;
x=x*x%mod;
}
return ans;
}
void init()//求阶乘+逆元
{
re[0]=1;
for(int i=1;i<=n;i++)
re[i]=re[i-1]*i%mod;
inv[n]=pow(re[n]);
for(int i=n-1;i>=0;i--)
inv[i]=inv[i+1]*(i+1)%mod;//o(n)求逆元
}
long long C(long long a,long b)
{
return re[a]*inv[b]%mod*inv[a-b]%mod;
}
int main()
{
// freopen("in.in","r",stdin);
// freopen("my.out","w",stdout);
long long ans=0,dase=1,print=0;s[0]=0;
scanf("%d%d",&n,&k);
scanf("%s",str+1);
for(int i=1;i<=n;i++) date[i]=str[i]-'0',s[i]=s[i-1]+date[i];
init();
for(int i=1;i<=n-k;i++)
{
ans=(ans+s[n-i]%mod*dase%mod*C(n-i-1,k-1)%mod)%mod;
ans=(ans+date[n-i+1]%mod*dase%mod*C(n-i,k)%mod)%mod;
dase=dase*10%mod;
}
printf("%lld\n",ans);
return 0;
}