首页 > 代码库 > 逆元(bzoj 2186)

逆元(bzoj 2186)

Description

  大富翁国因为通货膨胀,以及假钞泛滥,政府决定推出一项新的政策:现有钞票编号范围为1到N的阶乘,但是,政府只发行编号与M!互质的钞票。房地产第一大户沙拉公主决定预测一下大富翁国现在所有真钞票的数量。现在,请你帮助沙拉公主解决这个问题,由于可能张数非常大,你只需计算出对R取模后的答案即可。R是一个质数。

Input

第一行为两个整数T,R。R<=10^9+10,T<=10000,表示该组中测试数据数目,R为模后面T行,每行一对整数N,M,见题目描述 m<=n

Output

共T行,对于每一对N,M,输出1至N!中与M!素质的数的数量对R取模后的值

Sample Input

1 11
4 2

Sample Output

1

数据范围:
对于100%的数据,1 < = N , M < = 10000000
/*
  因为M<=N,所以M!|N!,我们很容易知道如下结论
     对于两个正整数m和n,如果n是m的倍数,那么1->n中与m互素的数的个数为(n/m)φ(m)
     本结论是很好证明的,因为1->m中与m互素的个数为φ(m),又知道(i,m)=(i+km,m),所以
     结论成立。那么对于本题,答案就是
     (N!/M!)φ(M!)=(N!/M!)M!(1-1/p1)(1-1/p2)...(i-1/pk)
                  =N!(1-1/p1)(1-1/p2)...(i-1/pk)
      其中pi为小于等于M的所有素数,先筛选出来即可。由于最终答案对一个质数取模,所以要用逆元,这里
      求逆元就有技巧了,用刚刚介绍的递推法预处理,否则会TLE的。
*/
#include<cstdio>
#include<iostream>
#include<bitset>
#define N 10000010
#define lon long long
using namespace std;
lon ans1[N],ans2[N],inv[N];
bitset<N> prime;
void get_prime(){
    prime.set();
    for(int i=2;i<N;i++){
        if(prime[i]){
            for(int j=i+i;j<N;j+=i)
                prime[j]=false;
        }
    }
}
int main(){
    get_prime();
    int MOD,m,n,T;
    scanf("%d%d",&T,&MOD);
    ans1[0]=1;
    for(int i=1;i<N;i++)
        ans1[i]=ans1[i-1]*i%MOD;
    inv[1]=1;
    for(int i=2;i<N;i++){
        if(i>=MOD)break;
        inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
    }
    ans2[1]=1;
    for(int i=2;i<N;i++){
        if(prime[i]){
            ans2[i]=ans2[i-1]*(i-1)%MOD;
            ans2[i]=ans2[i]*inv[i%MOD]%MOD;
        }
        else ans2[i]=ans2[i-1];
    }
    while(T--){
        scanf("%d%d",&n,&m);
        lon ans=ans1[n]*ans2[m]%MOD;
        printf("%lld\n",ans);
    }
    return 0;
}

 

逆元(bzoj 2186)