快速冪-(POJ3641,POJ1995)-算法筆記

快速冪原理

快速冪顧名思義就是快速計算xnx^n的方法。
我們用一個栗子來了解其原理。

xnx^n,首先我們試着用二進制表示n,如n=b1010
n=b10+b1000 即n=2+8
自然有xn=x2x8x^n =x^2*x^8

接着我們這樣想,n的二進制表示每一位都對應着x2,x4,x8x^2,x^4,…x^8,如果該位爲1則乘到結果中去,否則不乘。
即:
xn=x10+x21+x40+x81x^n=x^1*0+ x^2*1+ x^4*0+ x^8*1
上式很有規律,都利用到x2ix^{2^i}

試想如果我們求出了x2x^2之後保存這個結果,然後x4=x2x2x^4=x^2*x^2,保存x4x^4,接着計算x8=x4x4x^8=x^4*x^4
就可以求出二進制對應的每一位的值,然後再決定是乘0還是1。

這種方法的時間複雜度取決於n的二進制位數,即O(logn)
而從一開始xx乘到xnx^n次,這種時間複雜度是O(n)。

快速冪例子

typedef long long ll;
//x^n mod a快速冪方法
ll pow_mod(ll x,ll n, ll mod){
    ll res=1;
    while(n>0){
        if(n&1) res=res*x%mod;
        x=x*x%mod;
        n>>=1;
    }
    return res;
}

例題兩道

POJ3641

Description

Fermat’s theorem states that for any prime number p and for any integer a > 1, ap = a (mod p). That is, if we raise a to the pth power and divide by p, the remainder is a. Some (but not very many) non-prime values of p, known as base-a pseudoprimes, have this property for some a. (And some, known as Carmichael Numbers, are base-a pseudoprimes for all a.)

Given 2 < p ≤ 1000000000 and 1 < a < p, determine whether or not p is a base-a pseudoprime.

Input

Input contains several test cases followed by a line containing “0 0”. Each test case consists of a line containing p and a.

Output

For each test case, output “yes” if p is a base-a pseudoprime; otherwise output “no”.

#include <iostream>
using namespace std;
typedef long long ll;
//x^n 快速冪方法
ll pow_mod(ll x,ll n, ll mod){
    ll res=1;
    while(n>0){
        if(n&1) res=res*x%mod;
        x=x*x%mod;
        n>>=1;
    }
    return res;
}
bool ispri(long long num)
{
    for(int i=2;i*i<num;i++)
    if(num%i==0)
        return false;
    return true;
}
//POJ Pseudoprime numbers
int main(){
    while(true){
        ll p,a;
        cin >> p>>a;
        if(p==0 && a==0) break;
        ll res=pow_mod(a, p, p);
        if(res==a && !ispri(a))
            cout<<"yes"<<endl;
        else
            cout<<"no"<<endl;
        
    }
    return 0;
}

POJ1995

Description

人是不同的。有些人偷偷看充滿了有趣的女孩的雜誌,有些人在地窖裏製造原子彈,有些人喜歡使用WINDOWS,有些人喜歡數學遊戲。最新的市場調查顯示,這一細分市場目前被低估了,而且缺乏這樣的遊戲。這種遊戲因此被納入了科科達克運動。運動的遵循的規則:

每個玩家選擇兩個數字Ai和Bi,並把它們寫在一張紙條上。其他人看不到這些數字。在一個特定的時刻,所有的玩家都會向其他玩家展示他們的數字。目標是確定包括自己在內的所有玩家的所有表達的總和,並用給定的數字M確定除法後的餘數。獲勝者是首先決定正確結果的人。根據玩家的經驗,可以通過選擇更高的數字來增加難度。

你應該編寫一個程序來計算結果,並找出誰贏了這場比賽。

Input

輸入由Z賦值組成。它們的數目由出現在輸入的第一行上的單個正整數Z給出。然後分配如下。每個賦值都以包含一個整數M (1 <= M <= 45000)行開始。總數將除以這個數字。下一行包含玩家數H(1 <= H <= 45000)。接下來就是H行。在每一行上,正好有兩個數字Ai和Bi被空間隔開。兩個數不能同時等於零。

Output

對於每個組合,只有一條輸出線。在這條線上,有個數字,是表達的結果。

#include <iostream>
using namespace std;
typedef long long ll;
//x^n 快速冪方法
ll pow_mod(ll x,ll n, ll mod){
    ll res=1;
    while(n>0){
        if(n&1) res=res*x%mod;
        x=x*x%mod;
        n>>=1;
    }
    return res;
}

int main(){
    ll T;
    cin>>T;
    while(T>0){
        T--;
        ll M,H;
        cin>>M;
        cin>>H;
        ll res=0;
        for(int i=0;i<H;i++){
            ll a,b;
            cin>> a>>b;
            res=(res+pow_mod(a, b, M))%M;
        }
        cout<<res<<endl;
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章