关于快速幂和快速幂取余

例:BJFUOJ1193

fudq and Birthday Party

时间限制(普通/Java):1000MS/3000MS          运行内存限制:65536KByte
总提交:45            测试通过:22

描述

2014年1月ACM爱好者协会寒假集训期间,大家一定不会忘记的是17号下午在西配317开的生日party吧。
17号是某知名大牛lzwjava的生日,18号是某知名大牛cnsks的生日,于是fudq决定在17日下午开个生日party,庆祝两位大牛生日快乐。lzwjava和cnks给大家买了大蛋糕和好多好多吃的,桌上满满的全是吃的喝的,大家一起玩了好多游戏,场面相当嗨皮。A协其乐融融,所有人都好好玩了一下午。
最后大家一起点上蜡烛,唱生日歌,寿星许完愿之后当然是切蛋糕了,两位寿星亲自操刀,把蛋糕切好分给大家吃。fudq发现寿星切蛋糕有个规律,后面切的蛋糕比前面的大,而且从头到尾排列起来发现是个等比数列,但是光顾着嗨皮的fudq只记住了前两块蛋糕的大小,现在他想知道第K块蛋糕有多大?假设蛋糕有足够大。

输入

输入第一行是一个正整数T(1<=T<=30),表示接下来有T组测试数据。
接下来有T行,每行代表一组数据,每组数据包含有三个正整数a,b,K,其中a,b表示前两块蛋糕的大小,并且一定构成等比数列.1<=a<=b<=1,000,000,000,1<=K<=1,000,000,000,b%a=0.

输出

每组数据输出占一行,输出第K块蛋糕的大小,当然结果会很大,fudq只要求求出对20140119取余的结果。

样例输入

3
1 1 10
2 4 5
3 3000000 1000000000

样例输出

1
32
17398380

代码:

#include<stdio.h>
#define n 20140119
__int64 powhh(__int64 x,__int64 k,__int64 w)
{
    while(k>0)
    {
        if(k&1)
        {
            x*=w;
            x%=n;
            k--;
        }
        k/=2;
        w*=w;
        w%=n;
    }
    return x;
}
int main()
{
    __int64 a,b,k,x,q,w,t;
    //freopen("Data.txt","r",stdin);
    while((scanf("%I64d",&t))!=EOF)
    {
        while(t--)
        {
            scanf("%I64d%I64d%I64d",&a,&b,&k);
            q=b/a;
            k--;
            x=a%n;
            w=q%n;
            printf("%I64d\n",powhh(x,k,w));
        }
    }
    return 0;
}


总结:当有一个初始值要乘以很多个相同的数时,可以用快速降幂的方法。这里借用王博学长打出的快速幂函数:(自己按理解改动一点吧)

这个函数是算这么个东西的:x*m^n;(当n很大的时候,算这个式子很容易超时。。(当然也容易超范围,所以一般都会取余))


__int64 powhaha(__int64 x,__int64 n,__int64 m)     //m表示要乘的数,n表示要乘多少次。
{
    while(n>0)
    {
        if(n&1)     //<=>if(n&1>0)判断n是奇数还是偶数,如果是n是奇数,n&1=1,如果n是偶数,n&1=0;
        {
            x*=m;

            n--;     //为了好理解,这个是我自己加上去的。。。其实不加也可以,因为后面n/=2和(n--)/2;是等价的。

        }
        m*=m;  //然后同时乘以两个m。。。
        n/=2;   //既然同时乘了两个m,那肯定就只要乘以n/2次了。。
    }
    return x;
}


原理:

(ans=1)*n*n*···*n(m个n)

                     <------<---------------<---------------
if(m为偶数),那么可以化为                                       |
(ans=1)*(n*n)*(n*n)*···*(n*n)(m/2个(n*n))          |
if(m为奇数,把一个n放入ans中)                               |
(ans=n)*n*n*···*n(m-1个n)                                       |           循环,每次新的n等于原来的n*n,新的m=原来的m/2;
这是m-1为偶数,那么等式就可以简化为                   |
(ans=n)*(n*n)*(n*n)*···*(n*n)((m-1)/2个(n*n))   |
                      |                                                           |
                      --------------------->-----------------|快速幂再加上个取余就变成了快速幂取余。。。这样既不会超时也不会超范围了。。。




快速幂取余差不多就是(x*m^n)%k,原理是:(a*b)%c==((a%c)*(b%c))%c;

函数可以这样写:

__int64 powhaha(__int64 x,__int64 n,__int64 m,__int64 k)
{
    while(n>0)
    {
        if(n&1)
        {
            x*=m;
            x%=k;  
        }
        m*=m;
        m%=k;
        n/=2;
    }
    return x;
}


其实就是加了红色的两步。。。


好了,写完了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值