<题目链接>

<转载于 >>>  >

题目大意:
给出一串n个数字,让你在这串数字中添加k个 ' + ' 号(添加后表达式合法),然后所有拆分所得的所有合法表达式之和。

解题分析:

首先,暴力的做法肯定是不可行的,复杂度必然爆炸。然后来考虑怎么求每个数字在最终结果里的贡献呢。我们这n个数字有n-1个空位置,来放置k个' + '号,不论哪一种放置方法,每个数字都要在这种情况里出现一次,但是出现时所充当的分位是不同的,这就是统计贡献的地方!再由于每个数字都是不同的单独的,所以单独考虑每个数字可能所成为的位数时不会出现重复。

第n位:只能充当某个数字的个位有C(n-1,k)种个位情况

第n-1位:能充当个位(加号必须放一个在它后面才它能成为个位),能充当十位(加号必须放一个在它后面的后面才能使他成为十位并且他和个位之间不能放置东西)   个位:C(n-2,k-1)    十位:C(n-2,k)

第n-2位:同理   个位:C(n-2,k-1)   十位:C(n-3,k-1)  百位:C(n-3,k)

.........

也就是说:

倒数第一位:贡献了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)次千位数

........

倒数第n位:贡献了C(n-2,k-1)次个位数,C(n-3,k-1)次十位数,C(n-4,k-1)次百位数,C(n-4,k)次千位数......C(n - n,k)次n位数

所以我们预处理一下组合数C(x,k-1)和C(x,k),类似求个前缀和就可以啦!

核心部分:

倒数第一位:C(n-1,k)*s[n]

倒数第二位:C(n-2,k-1)*s[n-1] + C(n-2,k)*10*s[n-1]

倒数第三位:(C(n-2,k-1) + C(n-3,k-1)*10)*s[n-2] + C(n-3,k)*100*s[n-2]

倒数第四位:(C(n-2,k-1) + C(n-3,k-1)*10 + C(n-4,k-1)*100)*s[n-3] + C(n-4,k)*1000*s[n-3]

...........

所以可以看出,每一位数字的前部分(红色)和是从第n位累和过来的,后部分(绿色)是单独处理的,所以我们可以从第n位,开始往前循环每一位到第一位,累前缀和求和即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
const int M = 1e5+;
const int mod = 1e9+;
using namespace std;
typedef long long ll;
ll fac[M],inv[M],sum[M],a[M];
char s[M];
int n,k;
ll ans,mult;
ll mod_pow(ll x,ll n){
x%=mod;
ll res = ;
while(n>){
if(n&) res = res*x%mod;
x = x*x%mod;
n>>=;
}
return res;
}
void init(){
inv[]=;fac[]=;
for(int i=;i<=n;i++){
fac[i] = (fac[i-]*i)%mod;
inv[i] = mod_pow(fac[i],mod-)%mod; //费马小定理预处理记录 i! 的逆元
}
}
ll C(ll n,ll m){
if(n<||m<) return ;
if(m>n) return ;
return (((fac[n]*inv[m])%mod)*inv[n-m])%mod; //计算组合数C(n,m), n!*(m!的逆元)*((n-m)!的逆元)
}
int main()
{
scanf("%d%d",&n,&k);
init();
scanf("%s",s+);
for(int i=;i<=n;i++)
a[i] = s[i]-'';
mult = ;
for(int i=n-;i>=;i--){
sum[i] = (sum[i+]+mult*C(i-,k-))%mod; //计算该数位为红色部分的贡献,因为红色部分这一位与这一位+1的贡献有关,所以可以求部分前缀和,简化计算
mult = (mult*)%mod;
}
mult = ;
for(int i=n;i>=k+;i--){
sum[i] = (sum[i]+C(i-,k)*mult)%mod;//计算该位数绿色部分的贡献
mult = (mult*)%mod;
}
ans = ;
for(int i=;i<=n;i++)
ans = (ans+sum[i]*a[i])%mod; //乘上每个位数上的数
printf("%lld\n",ans);
}

2018-10-09

最新文章

  1. [LeetCode] UTF-8 Validation 编码验证
  2. spring boot properties
  3. Opencv结构与内容
  4. 深入理解maven及应用--转
  5. 《android基于andFix的热修复方案》实战篇
  6. VC更换图标文件
  7. 使用 Swift 制作一个新闻通知中心插件(2)
  8. SQLServer 2008数据库查看死锁、堵塞的SQL语句
  9. BAE 环境下配置 struts2 + spring + hibernate(SSH)(一)准备
  10. [操作系统] OS X Yosemite U盘制作
  11. Largest Rectangular Area in a Histogram
  12. [Android FrameWork 6.0源码学习] View的重绘过程
  13. Java高并发秒杀系统【观后总结】
  14. Shadow Copying导致ASP.NET应用启动很慢的解决办法
  15. Excel技巧--分隔工资条
  16. 关于如何使`(a === 1 &amp;&amp; a === 2 &amp;&amp; a === 3)`返回`true`问题的思考
  17. 第五章703N 刷openwrt 挂载u盘
  18. 【mysql】IP地址整数int和varchar的转换
  19. Tomcat学习总结(3)——Tomcat优化详细教程
  20. jQuery过滤选择器:not()方法介绍

热门文章

  1. sleep()和wait()的区别及wait方法的一点注意事项
  2. Walle,一个开源的web代码发布管理系统
  3. popup的简单应用举例(具体在增删改查组件中用到)以及补充的知识点
  4. 《剑指offer》 二叉树的镜像
  5. HTML&amp;javaSkcript&amp;CSS&amp;jQuery&amp;ajax(四)
  6. MySQL----数据库简单操作
  7. Allegro PCB Design GXL (legacy) 使用slide无法将走线推挤到焊盘的原因
  8. 模块(import语句,from...import语句,_name_属性)
  9. Jmeter中常用的一些对字符串的处理
  10. Android取得系统时间