`
s2003zy
  • 浏览: 8950 次
  • 性别: Icon_minigender_1
  • 来自: 赤峰
最近访客 更多访客>>
社区版块
存档分类
最新评论

cf 327c magic five 等比数列求和+求逆元 + 快速指数幂 使用 费马小定理求逆元

 
阅读更多
题意
        给你个板子板子上有一串数(一个字符串str+字符串重复的次数)让你去掉任意的数(不能全删)使之成为能够被5整除的数,问共有多少种方
        法(%1000000007)m= 1000000007
        PS   1) 允许有前导0
                2) 板子上不同位置的数去掉是不同的答案  比如 115 去掉第一个1和去掉第二个1是两种方法
分析
    1)可以被5整除的数:结尾为0或者5的数即可被5整除
    2)删除的方法:当第I位数为5或者0时  那么一共有2^i种方法(i起始于0)
                               一个str(不算重复的)中共有    a{1} = 2^0*(str[0]==5||str[0]==0)+……+2^i*(str[i]==5||str[i]==0).......
                               所有str(算上重复的)构成等比数列 
                                a{n}=a{1}*q^(n-1)
                                其中 q= 2^strlen(str).
                                和值:  sum = (a{1} *(q^n-1  )/(q-1) )%m
分析结果
        我们需要求的就是    sum = a{1} *(q^n-1  )/(q-1) %m 但是有两个重点问题需要解决
        1)求指数运算比较大直接算会超时: 可以使用快速指数幂来进行求快速的求解
        2)对于sum的球模 中间会有除法: 让我们想到求逆元使之变成乘法运算 
        3)转换为乘法:见下文中的费马定理求逆元之后
 
 
快速指数幂
       网上有很多 讲的很好 直接百度过来
      

        1)定义

快速幂顾名思义,就是快速算某个数的多少次幂。
其时间复杂度为 O(log2N), 与朴素的O(N)相比效率有了极大的提高。
以下以求a的b次方来介绍

2)原理

把b转换成2进制数
该2进制数第i位的权为(2^(i-1))
例如
a^11=a^(2^0+2^1+2^3)
11的二进制是1 0 1 1
11 = 2^3*1 + 2^2*0 + 2^1*1 + 2^0*1
因此,我们将a^11转化为算a^(2^0)*a^(2^1)*a^(2^3)

3)代码比较

常规求幂

int pow1( int a, int b ) {
int r = 1;
while( b-- )
r *= a;
return r;
}

二分求幂(一般)

int pow2( int a, int b ) {
int r = 1, base = a;
while( b != 0 ) {
if( b % 2 )
r *= base;
base *= base;
b /= 2;
}
return r;
}

快速求幂(位操作)

int pow3( int a, int b ) {
int r = 1, base = a;
while( b != 0 ) {
if( b & 1 )
r *= base;
base *= base;
b >>= 1;
}
return r;
}

费马定理求逆元

       费马小定理是数论中的一个重要定理,其内容为: 假如p是质数,且gcd(a,p)=1,那么 a^(p-1) ≡1(mod p) 假如p是质数,且a,p互质,那么 a的(p-1)次方除以p的余数恒等于1
 
上面的分析结果
转换为乘法      
            我们要求
                         sum = a{1} *(q^n-1  )/(q-1) %m
                         sum = a{1}%m*( (q^n-1)/(q-1))%m
                         其中a{1}%m很好算
                         剩下的问题只是temp= ( (q^n-1)/(q-1))%m
                         为了方便起见 设 x=(q^n-1)       y=(q-1)   y的逆元为yy(别急一会儿求)
                         剩下的问题就变成了求     temp=(x/y)%m  
                         已知逆元 y*yy%m==1(逆元的知识自己百度吧)
                          temp= (x/y)%m  = (x/y)%m *(y*yy)%m = ((x/y)*(y*yy))%m = (x*yy)%m
                          已知 gcd(y,m)=1 且 m是质数
                          由小费马定理可以知道
                          y^(m-1)%m=(  y*y^(m-2)   )%m=1
                          因此 y的逆元 即yy =y^(m-2)
                          又因为 temp = (x*yy)%m 
                           所以  temp =   (x*y^(m-2))%m
                          所以SUM = (a{1}*(x*y^(m-2)))%m
                               = (a{1}*( (q^n-1)* (q-1) ^(m-2)))%m
   所以::::把sum用快速指数幂算一遍就行了··············
                           
      AC代码:                   
 
#include<iostream>
#include<cstdio>
#include<stdlib.h>
#include<cstring>
using namespace std;
const long long M = 1000000007; 
long long pow3( long long a, long long b ) {
	long long r = 1, base = a;
	while( b != 0 ) {
		if( b & 1 )
			r = (r*base)%M;
		base = ( base * base ) % M;
		b >>= 1;
	}
	return r;
}
int main()
{
	char s[300000];
	long long n;
	while(gets(s))
	{
		cin>>n;
		getchar();
		long long sLen = strlen(s);
		long long sum = 0;
		long long base = 1;
		for(int i=0;i<sLen;i++)
		{
			if( s[i]=='0' || s[i] == '5' )
			{
				sum = ( sum + base )%M;
			}
			base = (base * 2)%M;
		}
		long long q = pow3(2,sLen);
		long long a1 = sum;
		int top = (pow3(q,n)+M-1)%M;
		int buttom = pow3((q+M-1)%M,M-2)%M;
		sum = (a1*top)%M;
		sum = (sum*buttom)%M;
		cout<<sum<<endl;
		
	}
}
 
 
分享到:
评论

相关推荐

    求逆元的C程序(算法)

    用c语言实现逆元的计算,通过自己设计算法代码实现。

    扩展欧几里得算法求逆元

    扩展欧几里得算法求逆元

    数论(组合数求法+Lucas定理+线性求逆元

    数论

    使用扩展欧几里得算法求乘法逆元

    求解乘法逆元的方法之一就是使用扩展欧几里得算法。 扩展欧几里得算法是欧几里得算法的一种扩展,不仅能够找到两个整数的最大公约数(GCD),还能同时计算出它们的贝祖等式解。贝祖等式是这样的形式:ax + by = gcd...

    信息安全与保密概论(华中科技大学)辗转相除法求模的逆元

    在信息安全与保密领域,计算模数的逆元是加密算法中的关键步骤,特别是在涉及模运算的密码系统中,如RSA公钥加密系统。模逆元的求解是数学中的一个基本问题,它与数论紧密相关。本篇将详细介绍如何使用辗转相除法,...

    CPP求逆元代码 扩展欧几里得

    用递归实现的求逆元的代码,使用中的算法是扩展的欧几里得算法。输入本元和模数,得到乘法逆元。

    【逆元六大常用高效算法】

    费马小定理是一种特殊情况下的逆元计算方法,当模数为素数时,a的逆元可以快速得到,即a^(c-2) mod c,其中c是素数。费马小定理的核心在于,当p为质数时,任何不为p的倍数的整数a的逆元就是a^(p-2) mod p。快速幂...

    辗转相除法计算乘法逆元及C语言实现-密码学

    3. **求解逆元**:当余数为1时,根据扩展欧几里得算法的原理,可以求出满足\( a \cdot x + n \cdot y = 1 \)的\( x \)和\( y \),其中\( x \)即为所求的乘法逆元。 下面是一段C语言代码示例,用于计算\( a \)模\( n...

    关于线性求逆元 算法

    关于 线性求逆元 算法的代码,自己写的,和大家分享,希望大家能多多指教

    组合计数 (四)1.递归 2. 快速幂求逆元 3.卢卡斯定理 4.阶乘分解加大数据乘法运算

    a,b较小时 2000 #include using namespace std; const int N = 2010, mod = 1e9 + 7; int c[N][N]; int n; int main() ... c[x][y] &lt;...a,b较大时 利用乘法逆元求 a,b为10^5 #include #include #inclu

    扩展的欧几里得算法(实现求乘法逆元)

    欧几里得是数论中的一个最初步的概念,它用来判断两个数的最大公因子,扩展的欧几里得能够进一步实现在两个数互素情况下的乘法可逆元。求可逆元是一些算法的基础。

    ,密码学扩展的EUCLID算法求逆元

    本文将详细介绍如何使用扩展欧几里得算法求解一个整数\( a \)在模\( f \)下的逆元,并提供一个基于VC++6.0的实现示例。 #### 扩展欧几里得算法原理 扩展欧几里得算法不仅能够求解两个整数的最大公约数(GCD),还...

    群的判断求逆元

    群判定,逆元,元素的阶 #include using namespace std; #define N 10000 //对四元群运算函数的定义 char ysuan(char a,char b) { char arr[4]={'a','b','c','e'}; int i=0,j=0; if(a==b) { return arr[3]; }...

    扩展欧几里得算法的matlab程序(求多项式的乘法逆元)

    在这个场景下,我们关注的是如何使用扩展欧几里得算法来求解多项式的乘法逆元,特别是在MATLAB环境下实现这一算法。 在环\[ \mathbb{Z}[x] \]中,如果有一个多项式\( f(x) \),它的乘法逆元是指存在另一个多项式\( ...

    用欧几里德求逆元(matlab)

    根据提供的文件信息,本文将详细解释如何利用欧几里德算法在MATLAB环境中求解逆元问题,并通过示例代码加深理解。 ### 欧几里德算法与逆元概念 #### 欧几里德算法 欧几里德算法(Euclidean Algorithm),又称为...

    乘法逆元(扩展欧几里得算法)

    ### 乘法逆元与扩展欧几里得算法 #### 一、乘法逆元的基本概念 乘法逆元在密码学中具有重要的地位,它指的是对于一个整数 \( a \) 在模 \( p \) 意义下存在一个整数 \( b \),使得 \( ab \equiv 1 \pmod{p} \) ...

    C语言实现计算乘法逆元

    用C语言简单实现乘法逆元计算的代码(!只能计算正整数)

    Delphi求乘法逆元

    计算乘法逆元有多种方法,包括扩展欧几里得算法、费马小定理和中国剩余定理等。扩展欧几里得算法是最常用的,因为它能同时找到最大公约数和逆元,时间复杂度为O(log n)。 在给定的压缩包文件"求乘法逆元"中,可能...

    求模逆元的一种算法

    在C语言中,我们可以使用扩展欧几里得算法来求解模逆元。 **扩展欧几里得算法**是求解最大公约数(GCD)的一个过程,同时也能得到模逆元。算法基于欧几里得定理:对于任何两个正整数a和b,存在整数x和y使得gcd(a, b...

    android多项式求乘法逆元小软件源码

    这个“android多项式求乘法逆元小软件源码”提供了一个实用的解决方案,尤其对于需要处理数学问题的开发者来说,具有很高的价值。下面我们将详细探讨相关知识点。 1. **多项式**: 多项式是数学中的基本概念,由...

Global site tag (gtag.js) - Google Analytics