第十四届华中科技大学程序设计竞赛(Beautiful Trees Cutting)二项式定理 & 快速幂 & 费马小定理求逆元

本文介绍了一种算法问题,即在给定的字符串中通过删除部分字符来形成可以被5整除的数字的所有可能方式,并计算这些方式的数量。文章详细解析了解决方案的思路,包括如何利用数学原理优化计算过程。


It’s universally acknowledged that there’re innumerable trees in the campus of HUST.


One day Xiao Ming is walking on a straight road and sees many trees line up in the right side. Heights of each tree which is denoted by a non-negative integer from 0 to 9 can form a tree string. It's very surprising to find that the tree string can be represent as an initial string repeating K times. Now he wants to remove some trees to make the rest of the tree string looks beautiful. A string is beautiful if and only if the integer it represents is divisible by five. Now he wonders how many ways there are that he could make it.

Note that when we transfer the string to a integer, we ignore the leading zeros. For example, the string “00055” will be seen as 55. And also pay attention that two ways are considered different if the removed trees are different. The result can be very large, so printing the answer (mod 1000000007) is enough. And additionally, Xiao Ming can't cut down all trees.

输入描述:


The first line contains a single integer K , which indicates that the tree string is the initial string repeating K times.
The second line is the initial string S .

输出描述:

A single integer, the number of ways to remove trees mod 1000000007.
示例1

输入

1
100

输出

6

说明

Initially, the sequence is ‘100’. There are
6 ways:
100
1_0
10_
_00
__0
_0_
示例2

输入

3
125390

输出

149796

题意:输入一个看k,表示 下面字符串要重复多少次;下面一行为字符串,在重复k次的字符串中去掉一些字符,使得能整除5,去掉字符不同,则为不同的方法,最后结果可能会很大,mod  1000000007

思路:能乘除5的一定是0或者5结尾,所以 只要当前字符是0或者5的话,那么就可以把它之前的 字符去掉任意改个;若当前字符是i的话,前面有i个字符,那么 从前面字符 选出 0 去掉,从中选出 1 去掉,从中选出 2去掉......从中选出 i个去掉


求逆元的解释: 点击打开链接

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define Max 100010
#define ll long long
const long long mod = 1000000007;
char str[Max];
ll p_pow(ll a,ll b)    // a^b 
{
	ll res = 1;
	while(b)
	{
		if(b&1) res = res*a%mod;
			b>>=1;
		a = a*a % mod; 
	}
	return res;
}
int main()
{
	ll i,j,k;
	while(~scanf("%lld",&k))
	{
		scanf("%s",str);
		ll l = strlen(str);
		ll q = p_pow(2,l);
		ll fz = p_pow(q,k);
		fz = ((1-fz)%mod + mod)%mod;
		ll fm = ((1-q)%mod+mod)%mod;
		ll temp = fz*(p_pow(fm,mod-2))%mod;    // 费马小定理求逆元; 
		ll res = 0;
		for(i = 0;i<l;i++)
		{
			if(str[i] == '5'||str[i]=='0')
			{
				//res += p_pow(2,i)*temp%mod;  // 我不知道这样写是不是溢出了,但是他就是不对了;
				res +=p_pow(2,i);  // 把 temp 提出来,先让括号内的相加; 
			}
		}
		res = (res%mod)*temp%mod;   
		printf("%lld\n",res);
	}
	return 0;
}


### 费马小定理快速幂求解乘法逆元中的应用 费马小定理是数论中的一个重要定理,其描述为:如果 $ p $ 是一个质数,且 $ b $ 不是 $ p $ 的倍数,则有: $$ b^{p-1} \equiv 1 \ (\text{mod} \ p) $$ 基于这一结论,可以推导出 $ b $ 的乘法逆元。具体来说,乘法逆元的定义是:若 $ b \cdot x \equiv 1 \ (\text{mod} \ p) $,则 $ x $ 是 $ b $ 在模 $ p $ 意义下的乘法逆元。根据费马小定理,可以进一步得出: $$ b \cdot b^{p-2} \equiv 1 \ (\text{mod} \ p) $$ 因此,$ b^{p-2} \ \text{mod} \ p $ 就是 $ b $ 的乘法逆元。 在实际计算中,为了高效求解 $ b^{p-2} \ \text{mod} \ p $,通常采用快速幂算法。快速幂通过将指数分解为二进制形式,并利用模运算的性质,将幂次运算的时间复杂度从 $ O(n) $ 降低到 $ O(\log n) $。这种方法特别适合处理大数幂运算,例如在模数 $ p $ 较大的情况下。 以下是一个使用快速幂算法求解乘法逆元的示例代码: ```cpp #include <iostream> using namespace std; // 快速幂算法(取模):a^b % p long long pow_mod(long long a, long long b, long long p) { long long result = 1; a %= p; while (b) { if (b &amp; 1) // 如果b是奇数 result = (result * a) % p; a = (a * a) % p; // 平方 b >>= 1; // 将b右移一位 } return result; } // 求a在模p下的乘法逆元(费马小定理,p必须为质数) long long inv(long long a, long long p) { return pow_mod(a, p - 2, p); } int main() { long long a, p; cout << "请输入a和p的值(p必须为质数):" << endl; cin >> a >> p; cout << a << "在模" << p << "下的乘法逆元是:" << inv(a, p) << endl; return 0; } ``` #### 代码说明 - `pow_mod` 函数实现了快速幂算法,用于计算 $ a^b \ \text{mod} \ p $。 - `inv` 函数调用 `pow_mod`,传入参数 $ a $、$ p-2 $ 和 $ p $,以计算 $ a^{p-2} \ \text{mod} \ p $,即 $ a $ 在模 $ p $ 意义下的乘法逆元。 - 在 `main` 函数中,用户输入 $ a $ 和 $ p $ 的值,程序输出对应的乘法逆元。 #### 适用条件 - 使用费马小定理的前提是模数 $ p $ 必须为质数。 - $ a $ 不能是 $ p $ 的倍数,否则无法满足 $ \text{gcd}(a, p) = 1 $,从而无法保证乘法逆元的存在。 通过快速幂算法,可以在 $ O(\log p) $ 的时间复杂度内高效地求解乘法逆元,这在实际应用中具有重要意义,尤其是在密码学和大数运算等领域。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值