【BZOJ】2186 沙拉公主的困惑

一道非常有價值的題。

【解析1】歐幾里德算法求乘法逆元,前綴和

[Analysis]O(T n log n)。

[Sum]
①int運算,如果會超出界,第一個數前要加上(LL)即類型轉換。
②gcd不變的歐幾里德定理:可以是加,也可以是減。

[Code]
/**************************************************************
    Problem: 2186
    User: y20070316
    Language: C++
    Result: Accepted
    Time:6496 ms
    Memory:157056 kb
****************************************************************/
 
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
 
typedef long long LL;
const int N=10000001;
 
int p[N],v[N],inv[N];   //screen
int pre[N]; //Prefix n!
int cas,r,n,m;  //Basic
int x,y;    //Exgcd
 
void exgcd(int i,int j)
{
    if (!j) {x=1,y=0;return;}
    exgcd(j,i%j);
    int x_=y,y_=x-(i/j)*y;
    x=x_,y=y_;
}
 
int main(void)
{
    scanf("%d%d",&cas,&r);
     
    for (int i=2;i<N;i++)
    {
        if (!v[i])
        {
            p[++p[0]]=i;
            exgcd(i,r),inv[i]=x%r;
        }
        for (int j=1;j<=p[0];j++)
        {
            if (i*p[j]>=N) break;
            v[i*p[j]]=1;
            if (i%p[j]==0) break;
        }
    }
     
    pre[0]=1;
    for (int i=1;i<N;i++)
        pre[i]=(LL)pre[i-1]*i%r;
     
    inv[1]=1;
    for (int i=2;i<N;i++)
        if (!inv[i])
            inv[i]=inv[i-1];
        else
        {
            inv[i]=(LL)inv[i]*(i-1)%r;
            inv[i]=(LL)inv[i]*inv[i-1]%r;
        }
     
    for (int cc=1;cc<=cas;cc++)
    {
        scanf("%d%d",&n,&m);
        printf("%d\n",((LL)pre[n]*inv[m]%r+r)%r);
    }
     
    return 0;
}</span>


【解析2】遞推求乘法逆元,前綴和

[Analysis]O(Tn)
性質:關於Mod M作用下i的逆元inv[i]=-(M/i)*inv[M%i]。
證明:
令a=M/i,b=M%i,
∴M=ai+b。
∴inv[i] = -a * inv[b]。
同餘式兩邊同時乘上i,得:
i * inv[i]
= -ai * inv[b]
= (b-M) * inv[b]
= b*inv[b]
= 1 (mod M)
∴inv[i]爲在Mod M下i的逆元,證畢。

O(n)求法比直接求所有素數的逆元還慢一些...
[Code]
/**************************************************************
    Problem: 2186
    User: y20070316
    Language: C++
    Result: Accepted
    Time:7700 ms
    Memory:196116 kb
****************************************************************/
 
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
 
typedef long long LL;
const int N=10000001;
 
int p[N],v[N];  //screen
int inv[N],sinv[N]; //Mutiplicative Inverse
int pre[N]; //Prefix n!
int cas,r,n,m;  //Basic
 
int main(void)
{
    scanf("%d%d",&cas,&r);
     
    for (int i=2;i<N;i++)
    {
        if (!v[i]) p[++p[0]]=i;
        for (int j=1;j<=p[0];j++)
        {
            if (i*p[j]>=N) break;
            v[i*p[j]]=1;
            if (i%p[j]==0) break;
        }
    }
     
    pre[0]=1;
    for (int i=1;i<N;i++)
        pre[i]=(LL)pre[i-1]*i%r;
     
    inv[1]=1;
    for (int i=2;i<N;i++)
        inv[i]=(LL)(r-r/i)*inv[r%i]%r;
     
    sinv[1]=1;
    for (int i=2;i<N;i++)
    {
        sinv[i]=sinv[i-1];
        if (!v[i]) 
        {
            sinv[i]=(LL)sinv[i]*(i-1)%r;
            sinv[i]=(LL)sinv[i]*inv[i]%r;
        }
    }
     
    for (int cc=1;cc<=cas;cc++)
    {
        scanf("%d%d",&n,&m);
        printf("%d\n",((LL)pre[n]*sinv[m]%r+r)%r);
    }
     
    return 0;
}</span>


下面是做這道題時做的一些筆記:

1.階乘的乘除
①直接計算。
②分解質因數。
如果有取餘,用①方便。
如果要寫高精度,用②方便。

2、歐拉函數的求法
例:求fai(60)
①分解質因數正規求法
60=2^2 * 3^1 * 5^1,
∴fai(60)=60 * (1-1/2) * (1-1/3) * (1-1/5) = 16。
②根據①的另一種求法
fai(60)= (1 * 2^1) * (2*3^0) * (4*5^0)= 16。
③積性函數的解法:可結合歐拉篩法達到O(n)求出所有。
fai(60) = fai(2^2) * fai(3^1) * fai(5^1) = 16。

3、乘法逆元(mutiplicative inverse)
(1)什麼是乘法逆元?
羣G中任意一個元素a,都在G中有唯一的逆元a',s.t. aa'=a'a=e,e爲單位元。
例:求4關於1模7的逆元,即求關於X的方程 4X ≡1 (mod 7)。
(2)怎麼求乘法逆元?
在求乘法逆元aa'=b(mod c)前,要滿足(a,c)=1即(a,c)互質。
①同餘方程 --> 不定方程 --> exgcd。 單個,O(log n)。
②歐拉定理
根據歐拉定理,當a與P互質時,a ^ fai(P) = 1 (mod P)。
∴a * a^(fai(P)-1) =1 (mod P)。
在mod P意義下a的乘法逆元a' = a^(fai(P)-1)。
特別的,當P爲質數時,a' = a^(P-2)。 單個,O(log fai(P)-1)。
③積性函數
乘法逆元是積性函數,可以線性篩(screen)。
對於素數考慮以上兩種方法哪種好。 所有,O(log n)或者O(fai(P)-1),一般來說用①。
④遞推法。 所有,O(n)。
關於遞推法,見:
http://blog.csdn.net/whyorwhnt/article/details/19169035。
(3)一個經典的問題:求(a/b) mod p。
性質:設b'爲b的逆元,即:b'b=1(mod p),那麼 a/b = a*b' (mod p)。
證:
∵b'b=1(mod p)
∴b'b=1+px即b'=(1+px)/b。
∴a*b'=a*(1+px)/b=a*(1+0)/b(mod p)=a/b (mod p),證畢。
這個性質在a變求和邊取模,然後求(a/b) mod p時有用。

發佈了137 篇原創文章 · 獲贊 4 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章