51 nod 加号分配 组合数学(逆元,快速幂)
2017-09-29 09:02
435 查看
1528 加号分配
题目来源: 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),因此可以利用该公式递推求出其他的逆元
说了这么多,就一句话:对于每一位分析它作为第i位对答案的贡献值。
总结:做题中一定要有拆的思想,因为涉及到方案数,所以很有可能和组合数学有关,而且在不同的情况下每一位的状态是不同的,所以应该把状态分开来看。
题目来源: 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),因此可以利用该公式递推求出其他的逆元
说了这么多,就一句话:对于每一位分析它作为第i位对答案的贡献值。
总结:做题中一定要有拆的思想,因为涉及到方案数,所以很有可能和组合数学有关,而且在不同的情况下每一位的状态是不同的,所以应该把状态分开来看。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #define mod 1000000007 #define N 100005 using namespace std; int n,k; long long date ,s ; char str ; long long re ,inv ; 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 =pow(re ); 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; }
相关文章推荐
- 51 NOD 1119 机器人走方格 V2(组合数学 + 逆元)
- [51Nod1528][Codeforces520E]加号分配-组合数学
- ACM学习历程—SNNUOJ 1116 A Simple Problem(递推 && 逆元 && 组合数学 && 快速幂)(2015陕西省大学生程序设计竞赛K题)
- [组合数学]51 Nod 1486——大大走格子
- uva 11609 - Teams(组合数学+快速幂)
- ACM学习历程—HDU5490 Simple Matrix (数学 && 逆元 && 快速幂) (2015合肥网赛07)
- CodeForces 51 E.Pentagon(组合数学)
- 【hdu 5894】【组合数学 lucas 费马小求逆元】 【m个人坐n个围成一周的位置每个人距离至少为k,求方法数】
- 51 NOD 1138 连续整数的和(简单数学公式)
- 【51nod】--机器人走方格V2(组合数学&&逆元&&费马小定理)
- codeforces 559C|51nod1486 Gerald and Giant Chess(组合数学+逆元)
- 51 NOD 1189 阶乘分数(素因子分解+推公式+求逆元)
- [51NOD](1130)N的阶乘的长度 V2(斯特林近似) ---数学
- hdu 5651 xiaoxin juju needs help (组合数学+逆元)
- UVA11609 - Teams(组合数学+快速幂)
- HDU 5321 Beautiful Set (莫比乌斯反演 + 逆元 + 组合数学)
- CodeForces - 554C Kyoya and Colored Balls (组合数学&逆元模板)
- [51NOD](1003)阶乘后面0的数量 ---数学
- 51node1677treecnt(组合数学,好题)
- SDUT 3895 fireworks 山东第八届ACM大赛C题(组合数学(杨辉三角)+逆元)