一道很有价值的题。

【解析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时实用。

posted on 2017-07-24 09:56  lxjshuju  阅读(185)  评论(0编辑  收藏  举报