1. 程式人生 > >牛客小白月賽9 A簽到 數論————逆元

牛客小白月賽9 A簽到 數論————逆元

轉自:出處

複習內容:逆元的相關知識

在開始之前我們先介紹3個定理:

1.乘法逆元(在維基百科中也叫倒數,當然是 mod p後的,其實就是倒數不是嗎?):

如果ax≡1 (mod p),且gcd(a,p)=1(a與p互質),則稱a關於模p的乘法逆元為x。

2.費馬小定理(定義來自維基百科):

假如a是一個整數p是一個質數,那麼a^p - a是p的倍數,可以表示為

a^p \equiv a \pmod{p}

如果a不是p的倍數,這個定理也可以寫成

a^{p-1} \equiv  1 \pmod{p}

3.

(定義來自維基百科):

已知整數a、b,擴充套件歐幾里得演算法可以在求得a、b的最大公約數的同時,能找到整數x、y(其中一個很可能是負數),使它們滿足貝祖等式ax + by = \gcd(a, b)

好了,在明白上面的定理後我們開始分析乘法逆元:ax≡1 (mod p) 這個等式用中文描述就是 a乘一個數x並模p等於1,即 a%p*x%p=res,res%p=1;看上去就是同餘定理的一個簡單等式- -。那麼問題來了。

為什麼可以用費馬小定理來求逆元呢?

由費馬小定理 ap-1≡1 , 變形得 a*ap-2≡1(mod p),答案已經很明顯了:若a,p互質,因為a*ap-2≡1(mod p)且a*x≡1(mod p),則x=ap-2(mod p),用快速冪可快速求之。

為什麼可以用擴充套件歐幾里得求得逆元?

我們都知道模就是餘數,比如12%5=12-5*2=2,18%4=18-4*4=2。(/是程式運算中的除)

那麼ax≡1 (mod p)即ax-yp=1.把y寫成+的形式就是ax+py=1,為方便理解下面我們把p寫成b就是ax+by=1。就表示x是a的模b乘法逆元,y是b的模a乘法逆元。然後就可以用擴充套件歐幾里得求了。

知道逆元怎麼算之後,那麼乘法逆元有什麼用呢?

做題時如果結果過大一般都會讓你模一個數,確保結果不是很大,而這個數一般是1e9+7,而且這個數又是個素數,加減乘與模運算的順序交換不會影響結果,但是除法不行。有的題目要求結果mod一個大質數,如果原本的結果中有除法,比如除以a,那就可以乘以a的逆元替代。(除一個數等於乘它的倒數,雖然這裡的逆元不完全是倒數,但可以這麼理解,畢竟乘法逆元就是倒數的擴充套件)。

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

void exgcd(ll a,ll b,ll& d,ll& x,ll& y)
{
    if(!b) { d = a; x = 1; y = 0; }
    else{ exgcd(b, a%b, d, y, x); y -= x*(a/b); }
}

ll inv(ll a, ll p)
{
    ll d, x, y;
    exgcd(a, p, d, x, y);
    return d == 1 ? (x+p)%p : -1;
}

int main()
{
    ll a,p;
    while(1)
    {
        scanf("%lld %lld",&a,&p);
        printf("%lld\n",inv(a,p));
    }
}

題目描述

你在一棟樓房下面,樓房一共有n層,第i層每秒有pi的概率會扔下一個東西並砸到你
求第一秒內你被砸到的概率

輸入描述:

第一行一個整數n
之後有n行,第i+1行有兩個整數ai,bi,表示

輸出描述:

設答案為,你只需要找到一個最小的非負整數T,使得
輸出這個T就行了

示例1

輸入

複製

2
1 2
1 2

輸出

複製

750000006

說明

一共只有如下狀態:

1. 第一層和第二層都扔了下來

2. 第一層扔了下來

3. 第二層扔了下來

4. 第一層和第二層都沒有扔下來

以上四種都是等概率發生的

除了第四種情況外,都會被砸到

因此被砸到的概率是 3/4,這個值在模1e9+7意義下就是750000006

備註:

資料範圍
0 ≤ n ≤ 105
1 ≤ ai ≤ bi ≤ 105

此程式碼為學長的,自己的程式碼總是不對,就一點一點改,到頭來忘了取模了。小辣雞“”“”“”“”“”

#include<bits/stdc++.h>
using namespace std;
int mod=1e9+7;
typedef long long ll;

inline ll pow_mod(ll n,ll k)//n^k%mod
{
    ll res=1;
    n=n%mod;
    while(k>0)
    {
        if(k&1)
            res=res*n%mod;
        n=n*n%mod;
        k>>=1;
    }
    return res;
}
int main()
{
    ll n,p=1,q=1,a,b;
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&a,&b);
        p=p*(b-a)%mod;
        q=q*b%mod;
    }
    ll ans=((q-p+mod)*pow_mod(q,mod-2))%mod;
    printf("%lld\n",ans);
    return 0;
}

同上:不用看

#include<bits/stdc++.h>
using namespace std;
int mod=1e9+7;
typedef long long ll;

inline ll pow_mod(ll n,ll k)//n^k%mod
{
    ll res=1;
    n=n%mod;
    while(k>0)
    {
        if(k&1)
            res=res*n%mod;
        n=n*n%mod;
        k>>=1;
    }
    return res;
}
int main()
{
    ll n,a=1,b=1,a1,b1;
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&a1,&b1);
        a=a*(b1-a1)%mod;
        b=b*b1%mod;
    }
    ll temp=b-a+mod;
    printf("%lld\n",((temp*pow_mod(b,(mod-2)))%mod));
    return 0;
}