#295(div.2)E.Pluses everywhere

1.题目描述:点击打开链接

2.解题思路:本题是一道组合数学题,一开始用递归的思想做,但结果错误。学习了别人的解法后,豁然开朗。正确的解法是关注每一位数对整体的贡献值。比如输入的n位数是D1D2D3...D(n-1)D(n),那么当D(i)作为个位数时,它的前面必然有一个‘+’。剩下的k-1个‘+’被安置在剩下的n-2个空隙中,因此一共有C(n-2,k-1)种情况,D(i)的总贡献值是D(i)*C(n-2,k-1);同理,当D(i)作为十位数时,D(i+1)必然是个位数,D(i+1)前面必然有一个‘+’,那么剩下的k-1个‘+’被安置在剩下的n-3个空隙中,因此有C(n-3,k-1)种情况,D(i)的总贡献值是D(i)*10*C(n-3,k-1),以此类推。这样的D(i)最多只能是在第n-k位(个位算第1位,十位算第2位,依次类推),这样便计算出了第一部分贡献值。

第二部分的贡献值来自‘+’后面的那位数。假设D(i+1)前有一个‘+’且它恰好是第n位时,必然是k个‘+’被安置在它前面的n-1个空隙中,因此有C(n-1,k)种情况,那么D(i+1)的总贡献值是D(i+1)*C(n-1,k);若D(i+1)前有一个‘+’且它恰好是第n-1位,那么总贡献是D(i+1)*10*C(n-2,k),以此类推。最后,将两部分之和相加就是最终的答案。

由于涉及组合数取模,前缀和。因此应该事先在初始化中计算出这些数的值,便于后续处理。根据组合数公式:C(n,k)=n!/k!/(n-k)!,因此应当事先计算出n!(mod M)的值。由于本题中M是一个素数,可以考虑用逆元来代替除法。根据费马小定理易知:n^(M-1)≡1(mod M),即n*n^(M-2))≡1(mod M)。因此n的逆元是n^(M-2)。这是本题第一个新知识点。计算出了n!的逆元之后,再根据n!=n*(n-1)!,两边同时乘以INV(n),INV(n-1),化简得:INV(n-1)=n*INV(n),因此可以利用该公式递推求出其他的逆元,这是本题的第二个新知识点。本题通过考虑每一位数贡献值的角度则是第三个新知识点。

注意:输出long long型时推荐用%I64d输出。

3.代码:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;

typedef long long LL;
const int MOD = 1000000000 + 7;
const int N = 110000;
int n, k, a[N],sum[N];
char st[N];
LL fac[N], inv[N];

LL pow_mod(LL a, LL k)
{
	LL ans = 1;
	while (k > 0)
	{
		if (k & 1)ans = ans*a%MOD;
		a = a*a%MOD;
		k >>= 1;
	}
	return ans;
}
void init()//计算阶乘取模,逆元
{
	fac[0] = 1;
	for (int i = 1; i <= n; i++)
		fac[i] = fac[i - 1] * i%MOD;
	inv[n] = pow_mod(fac[n], MOD - 2);
	for (int i = n - 1; i >= 0; i--)
		inv[i] = inv[i + 1] * (i + 1) % MOD;
}
int C(int n, int k)//计算组合数取模
{
	return fac[n] * inv[k] % MOD*inv[n - k] % MOD;
}
int main()
{
	//freopen("t.txt", "r", stdin);
	scanf("%d%d", &n, &k);
	scanf("%s", st + 1);
	sum[0] = 0;
	for (int i = 1; i <= n; i++)
	{
		a[i] = st[i] - '0';
		sum[i] = sum[i - 1] + a[i];
	}
	init();
	LL ans = 0;
	LL s;
	LL base = 1;//base一定要设为LL!
	for (int i = 1; i <= n - k; i++)
	{
		s = (LL)sum[n - i] * base%MOD;//第一部分的贡献
		ans += (LL)s*C(n - i - 1, k - 1) % MOD;

		s = (LL)a[n - i + 1] * base%MOD;//第二部分的贡献
		ans += s*C(n - i, k) % MOD;

		base *= 10;
		ans %= MOD;
		base %= MOD;
	}
	printf("%I64d\n", ans);
	return 0;
}



时间: 2024-12-28 22:53:46

#295(div.2)E.Pluses everywhere的相关文章

Codeforces Round #295 (Div. 1) C. Pluses everywhere (组合数学+乘法逆元)

这题可以这样想: 对于当前第i位来说,该位若在个位上出现,那么第i位和第i+1位中间肯定有一个"+",剩下的k-1个"+"分布在剩下的n-2个空隙中,所以出现的总次数是C(n-2,k).同理,在十位上出现的总次数是C(n-3,k).于是每个数字的贡献值就可以求出来了,累加即可. 所以大体思路是遍历所有可能出现的位数,从个位开始,分成两部分计算,一部分用前缀和计算出前面所有的在该位上的贡献和,另一部分算出当前位置在该位上的贡献值. 然后对于求组合数,可以先将阶乘预处理

Codeforces Round #295 (Div. 2)

A. Pangram 题意:给出长度为n的字符串,判断26个字母(比如a,A都算作a出现了)是否都在该串中出现了 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include <cmath> 5 #include<algorithm> 6 using namespace std; 7 8 typedef long long LL; 9 10 11 12 int mai

#295(div.2) C.DNA Alignment

1.题目描述:点击打开链接 2.解题思路:比赛时没有想到好的思路,后来才发现,只需要t串中的字符是s串中出现次数最多的字符即可,根据乘法原理可知:最终结果是pow(num,n),其中num是s串中次数最多的字符的个数. 3.代码: #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<algorithm> #include<string> #include<sstream> #include&

Codeforces Round #295 (Div. 2) A+B+C

A题:找出字符串中是否出现了26个英文字母,不区分大小写 #include <cstdio> #include <cstring> #include <algorithm> using namespace std ; char str[200] ; int a[30] , b[30] ; int main() { int n , i ; scanf("%d", &n) ; scanf("%s", str) ; memset

Codeforces Round #295 Div1 C(Pluses everywhere)

Problem 给一个有n位的数A,现要求在A中插入k个加号,使得A被分成k+1份.比如1234,插入2个加号,可变为1+234,12+34,123+4.问所有插入情况得到的数之和(mod 1e9+7),比如1+234=235,12+34=46,123+4=127–>answer=235+46+127 Limits TimeLimit(ms):3000 MemoryLimit(MB):256 n,k∈[1,105],k<n Look up Original Problem From here

cf 295 div 2 B (bfs)

题意:给出 n.m 两数,可以对 n 进行两种操作 减一或者乘二,操作过程中 n 必须保证非负,问使 n 变为 m 至少需要几步操作. 这是我练水题的时候做到的,题目不难,只是我 bfs 一直没怎么用过比较不熟练,RE 了两发,就整理了这题. 首先,若 n==m ,那么步骤数就是 0 ,而若 n > m ,n 进行乘二的操作就会使 n 离 m 更远,所以必然是一直减一,于是步骤数就是 n -m : 若 n < m , 此时由于中间的步骤并不确定,就可以用 bfs 搜索,但是由于使用数组记录某个

C. DNA Alignment 数学公式推导 Codeforces Round #295 (Div. 2)

C. DNA Alignment time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Vasya became interested in bioinformatics. He's going to write an article about similar cyclic DNA sequences, so he invente

【记忆化搜索】Codeforces Round #295 (Div. 2) B - Two Buttons

题意:给你一个数字n,有两种操作:减1或乘2,问最多经过几次操作能变成m: 随后发篇随笔普及下memset函数的初始化问题.自己也是涨了好多姿势. 代码 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #define INF 0x7fffffff; 6 using namespace std; 7 int DP[20100], vis[2010

Codeforces Round #295 (Div. 1) B. Cubes (STL+类拓扑)

最近课业繁重,这题写了两天..昨晚睡觉的时候才突然想到了最后一点的解决方法. 不知道该不该叫做拓扑..感觉还是挺像的..就把标题称之为类拓扑了..这题的方法是用map来标记状态是否存在,然后用类似拓扑的方法不断的找拿走后依然稳定的方块,我用了两个优先队列来维护,分别取最大和最小.然后就是模拟这个过程取方块了. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queu