JZOJ1227 Coprime 题解

JZOJ1227 Coprime 题解


Description
对于两个整数k 和m,如果k 和m 的最大公约数为1,则k 和m 互质。给出两个正整
数n 和m(m≤n),定义f(n,m)为1~n!中与m!互质的数的个数。其中n!=123..(n-1)*n。
Task:给定n 和m,要求计算f(n,m)。

Input
本题设多组数据。
输入文件的第一行有一个整数T(1≤T≤100000),表示有T 组数据。
接下来有T 行,每行两个整数n和m(2≤n≤100000,2≤m≤n)。

Output
输出文件包含T 行,每行一个整数,表示f(n,m)。
由于答案过大,所以你只要输出f(n,m) mod 131071。
131071 是M17(梅森素数,2^17-1)。

Sample Input
1
3 2

Sample Output
3

思路:
首先引出一个定理:若$gcd(a,b)=1$则$gcd(a+b,b)=1$。
这个定理的证明可以参考我们老祖宗发明的“更相减损法”。
我们知道$1$~$m!$中与$m!$互质的数的个数可以表示为$\phi(m!)$$(\phi 表示欧拉函数)$
那么怎么表示$1$~$n!$中与$m!$互质的数的个数呢?
设$1$~$m!$中有一个数$k$与$m!$互质,根据前面的定理得到$gcd(k+m!,m!)=1$,因为$k<m!$(这不废话),所以每一个$gcd(k,m!)1$的$k$都能对应到$m!+1$~$2(m!)$中,也就是说$m!+1$~$2(m!)$中与$m!$互质的数的个数就是$\phi(m!)$,进而可以推出:$1$~$n!$中与$m!$互质的数的个数为$$\frac{n!}{m!}\phi(m!)$$
根据欧拉函数的计算式得:
$$\frac{n!}{m!}\phi(m!)=\frac{n!}{m!}{m!}\prod_{p|m!且p为质数}(1-\frac{1}{p})=n!\prod_{p|m!且p为质数}(1-\frac{1}{p})$$
由于$n! mod P$很容易求,我们需要关注的只是后面的这个式子,记其为$f(m)$:
$$f(m)=\prod_{p|m!且p为质数}(1-\frac{1}{p})$$
我们知道$m! = (m-1)! * m$,因此$f(m)$可以由$f(m-1)$推来。
从$(m-1)!$到$m!$只多乘了一个m,因此我们只需要考虑m。若m是质数,那么$f(m)=f(m-1)(1-\frac{1}{m})$,否则$f(m)=f(m-1)$。
这样就能用线性筛法预处理出每一个$f$了,同样$n!$也可以预处理,答案就是$n!
f(m)$
Code:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>

typedef long long ll;
const int N = 1e5 + 3;
const ll P = 131071;

int t, tot = 0, check[N], prime[N / 10];
ll n, m, jc[N], phi[N];

ll pow(ll x, ll p) //快速幂求逆元
{
    ll ret = 1;
    while (p)
    {
        if (p & 1) ret = ret * x % P;
        x = x * x % P;
        p >>= 1;
    }
    return ret;
}

int main()
{
    freopen("coprime.in", "r", stdin);
    freopen("coprime.out", "w", stdout);

    jc[1] = 1; for (int i = 2; i <= N - 3; i++) jc[i] = jc[i - 1] * i % P; //预处理n!
    phi[1] = 1;
    for (int i = 2; i <= N - 3; i++) //用线筛计算f
    {
        phi[i] = phi[i - 1];
        if (!check[i]) phi[i] = phi[i] * (i - 1) % P * pow(i, P - 2) % P, prime[++tot] = i; //i为质数,递推
        for (int j = 1; j <= tot; j++)
        {
            if (i * prime[j] > N - 3) break;
            check[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
        }
    }
    scanf("%d", &t);
    while (t--)
    {
        scanf("%lld%lld", &n, &m);
        printf("%lld\n", phi[m] * jc[n] % P); //答案就是n!*f(m)
    }

    fclose(stdin);
    fclose(stdout);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zjlcnblogs/p/8921212.html
今日推荐