【Sum(费马小定理+指数循环节公式)】

来源:互联网 发布:linux redis 安装配置 编辑:程序博客网 时间:2024/04/27 13:20

题目链接 HDU4704

【题意】就是求2^(n-1) % 1000000007 (n <= 10^100000)

【分析】由于n有100000位,所以直接快速幂也肯定超时,需要优化。

这里要使用到费马小定理;

费马小定理: gcd(a,m)=1(a,m互质) && m为素数,则a^(m-1)≡1 (mod m)

还需要用到一个公式(指数循环节):a^n  a^(n % Phi(M) + Phi(M)) (mod M) (n >= Phi(M));Phi(M)在M为素数时为M-1,n>=Phi(M)的限制可以不用考虑(当然考虑了不会有错,n<Phi(M)的话完全可以直接用快速幂了,我测试了几组数据结果都是一样的)。

有关这个公式的证明可以参考(数学渣表示看不懂)http://hi.baidu.com/aekdycoin/item/e493adc9a7c0870bad092fd9

这样在这里这个公式可以写成 a^n ≡ a^(n%(m-1)+m-1)  (mod m)(直接用这个公式复杂度已经很低了)

然后 因为1000000007是素数,并且gcd(2,1000000007) = 1,所以可以结合费马小定理化简上式:

a^n a^(n%(m-1)) * a^(m-1) a^(n%(m-1)) (mod m) 

这样求2^n-1 %1000000007只要求出(2^(n-1)%(1000000006)) %1000000007就可以了

【AC CODE(费马小定理+循环节公式)】0ms

#include <cstdio>#include <cstring>#include <cmath>typedef long long LL;#define MAXN 100010#define MOD 1000000007char c[MAXN];int pow_mod(LL a, int n){    LL ans = 1;    while(n)    {        if(n&1) ans = ans*a%MOD;        a = a*a%MOD;        n >>= 1;    }    return ans;}int main(){#ifdef SHY    freopen("e:\\1.txt","r",stdin);#endif    while(~scanf("%s%*c", c))    {        LL n = 0;        for(int i = 0;c[i];i++)            n = (n*10%(MOD-1)+c[i]-'0')%(MOD-1);        printf("%d\n", pow_mod(2,(n-1+MOD-1)%(MOD-1)));    }    return 0;}


 其实可以只用循环节公式a^(n%(m-1)) * a^(m-1)就能简化了

【AC CODE(循环节公式)】0ms

#include <cstdio>#include <cstring>#include <cmath>typedef long long LL;#define MAXN 100010#define MOD 1000000007char c[MAXN];int pow_mod(LL a, int n){    LL ans = 1;    while(n)    {        if(n&1) ans = ans*a%MOD;        a = a*a%MOD;        n >>= 1;    }    return ans;}int main(){#ifdef SHY    freopen("e:\\1.txt","r",stdin);#endif    while(~scanf("%s%*c", c))    {        LL n = 0;        for(int i = 0;c[i];i++)            n = (n*10%(MOD-1)+c[i]-'0')%(MOD-1);        printf("%I64d\n", ((LL)pow_mod(2,(n-1+MOD-1)%(MOD-1))*pow_mod(2,MOD-1))%MOD);    }    return 0;}


 

0 0