快速幂

今天学习快速幂。

定理一:积的取余等于取余的积的取余。即a%c*b=a*b%c;

定理二:

1.如果b是偶数,那么ans = (a^2 mod c)^(b/2);

2.如果b是奇数,那么ans=(a^2 mod c)^(b/2)*a;

由此,我们可以得出快速幂的算法为一下。

 1 int PowerMod(int a, int b, int c){
 2     int ans = 1;
 3     a = a % c;
 4     while(b>0)
 5     {
 6         if(b % 2 = = 1)
 7             ans = (ans * a) % c;
 8         b>>1;
 9         a = (a * a) % c;
10     }
11     return ans;
12 }

然后,让我们来做一道题来巩固一下。

Beer Barrels
  Finally, you got into the cellar of your preferred brewery where you expected many large piles of beer barrels to be stored. You are eager to inspect the barrels and maybe even their content (a lot and lot of content, actually...). Unfortunately, you fifind only fifive barrels, all hopelessly empty and dry. Some numbers are painted on the fifirst four barrels, one number on each of them. A note is attached to the fififth barrel. Behind the barrels in the dark, there is some low and barely discernible door in the wall, leading quite obviously to another lower cellar where you hope a whole slew of full barrels is kept hidden. The door is locked with a heavy and complex looking lock. With no obvious further constructive action in mind, you sit down to study the note on the fififth barrel.
  Its essence is the following.
  Denote the numbers painted on the fifirst, second, third and fourth barrel by A, B, K and C.
  Numbers A, B and C are just single digits.
  Now imagine that in the distant future some incredibly powerful computer (powered by quantum yeast) prints a list of all numbers which have exactly K digits and in which each digit is equal to A or B. Another equally mighty computer then takes the list and also the value C as the input and calculates the number of occurrences of digit C in the whole list.
  The resulting number taken modulo 1 000 000 007 is to be typed into the door lock to open it and to gain access to the lower cellar.
  You decide to calculate that number in your notebook you took with you.
  Input Specifification
  The input consists of a single line with four integers A, B, K, C (1 ≤ A, B, C ≤ 9, 0 ≤ K ≤ 1000),which represent the numbers painted on the fifirst four barrels.
  Output Specifification
  Output a single integer which opens the door lock.
  Sample Input 1
  1 2 3 2
  Output for Sample Input 1
  12
  Sample Input 2
  6 5 2 6
  Output for Sample Input 2
  4
以上。
看不懂可以找谷歌翻译。我就不翻译了。
题目的意思不算太复杂,讲的就是一个排列组合问题,如果k不算大,那么这个问题会很简单,在数学高考里就是送分题。
分析样例:1 2 3 2
根据题目我们推出样例中所有的 3 位数为:
111  共 0 个 2
211 121 112  共 3 个 2
221 212 122  共 6 个 2
222  共 3 个 2
所以本题答案为 0+3+6+3=12。
考虑除 111 的其他数字,第二行的数中每个数都包含一个二,也就是说,第二行的数都是
从 3 位数的其中一位将其设为 2,很明显可以联想到组合数,就是从三个位置选一个位置为 2
的所有情况,这种数的个数 C(3,1),然后每个数都有一个 2,所以答案为 C(3,1)*1,所以第
三行为 C(3,2)*2,第三行为 C(3,3)*3。
所以可以推出,最终答案为ΣC(K,i)*i,i∈[1,K]。
求组合数的公式为:

所以我们很容易就想到了打表,将到1000的阶乘都给存起来。不过直接存会溢出,所以我们要存取余以后的数。

不过这样的话,C(K,i)就不能求了。这里我们引入逆元的概念。

在数论中,如果[公式] ,我们就说 [公式] 和 [公式] 在模 [公式]意义下互为乘法逆元,记作 [公式] 。

也就是分母部分等于m!(n-m)!的逆元。

而快速幂+费马小定理可以方便地求逆元。

 1 // 快速幂求逆元
 2 LL pow_mod(LL a, LL b, LL p){//a的b次方求余p 
 3      LL ret = 1;
 4      while(b){
 5          if(b & 1) ret = (ret * a) % p;
 6          a = (a * a) % p;
 7          b >>= 1;
 8      }
 9      return ret;
10  }
11 LL Fermat(LL a, LL p){//费马求a关于b的逆元
12          return pow_mod(a, p-2, p);
13  }
以下是解法。
#include<cstdio> 
#include<iostream> 
#include<cstdlib> 
using namespace std; 
typedef long long ll; 
const ll mod=1e9+7;
ll qpow(ll a,ll b,ll mod)//快速幂 {
    ll ans=1%mod; 
    while(b) {
         if(b&1) ans=(ans%mod*a%mod)%mod; 
        a=(a%mod*a%mod)%mod; 
        b>>=1;
     }
    return ans;
}
int main() {
    ll f[1007]; 
    f[0]=f[1]=1;
    for(int i=2;i<=1000;i++)f[i]=(f[i-1]*i)%mod;//打表获得阶乘
    ll a,b,k,c, tmp,ans=0;
    cin>>a>>b>>k>>c; 
    if(a!=c&&b!=c)cout<<0<<endl; 
    else if(a==b)cout<<k<<endl; 
    else { 
        for(int i=1;i<=k;i++) { 
            tmp=(i*f[k]%mod*qpow(f[k-i],mod-2,mod)%mod*qpow(f[i],mod-2,mod)%mod)%mod;//i*组 合数 
            ans=(ans%mod+tmp%mod)%mod; 
        }
        cout<<ans<<endl; 
    }
    return 0; 
}
原文地址:https://www.cnblogs.com/zyyz1126/p/12348564.html