D. Same GCDs【CF 1295】

传送门

思路:

我们知道b = [a, a + m], 我们需要满足Gcd(a, m) = Gcd(b, m), 假设g = Gcd(a, m),那么Gcd(a, m) = Gcd(a + k * g, m)(a + k * g ∈ b),两边同除以g,

Gcd(a / g, m / g) = Gcd(a / g + k, m / g) = g / g = 1  < ====>  Gcd(b / g, m / g) = 1, 说明我们需要在b / g = [(a + g - 1) / g, (a + m) / g] 找到与 m / g互质的个数即可。

这里需要用到欧拉函数的相关知识,不会的学要自己学习。这样的话我们只需要求出( b/g右区间到1与m/g互质的个数) - (b/g左区间减一与m/g互质的个数)即可

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <string>
#include <map>
#include <set>
#define LL long long
using namespace std;

long long Gcd(LL a, LL b)
{
    return b == 0 ? a :Gcd(b, a % b);
}

void solve()
{
    int T;
    scanf("%d", &T);

    while(T--) {
        long long a, m, x, y, k, g, cp_k;
        scanf("%lld%lld", &a, &m);

        g = Gcd(a, m);
        x = (a + g - 1) / g;
        y = (a + m) / g;
        k = m / g;

        ///printf("x = %lld y = %lld k = %lld
", x, y, k);
        long long res[2];
        res[0] = y;
        cp_k = k;
        for(LL i = 2; i * i <= k; ++i) {
            if(cp_k % i == 0) {
                res[0] = res[0] / i * (i - 1);
                while(cp_k % i == 0) { cp_k /= i; }
            }
        }
        if(cp_k > 1) res[0] = res[0] / cp_k * (cp_k - 1);

        ///cout << "43 lines" << endl;

        res[1] = x - 1;
        cp_k = k;
        for(LL i = 2; i * i <= k; ++i) {
            if(cp_k % i == 0) {
                res[1] = res[1] / i * (i - 1);
                while(cp_k % i == 0) { cp_k /= i; }
            }
        }
        if(cp_k > 1) res[1] = res[1] / cp_k * (cp_k - 1);
        ///cout << res[0] << " " << res[1] << endl;
        printf("%lld
", res[0] - res[1]);
    }
}

int main()
{

    solve();

    return 0;
}
原文地址:https://www.cnblogs.com/SSummerZzz/p/14166701.html