SPOJ

传送门

t组数据 给定N和M

求解GCD(1, 1) * GCD(1, 2) * ... * GCD(1, M) * GCD(2, 1) * GCD(2, 2) * ... * GCD(2, M) * ... * GCD(N, 1) * GCD(N, 2) * ... * GCD(N, M).

(1 <= n, m <= 10000000) (1 <= t <= 5).

不要陷入误区,求的是gcd,可以是合数,但这个合数必然可以分解为质数相乘,所以最后的答案也一定可以转化成素数相乘,那么我们考虑每个素数的指数。

求解gcd(2 * pk, j), 当j是2的倍数时gcd可分解质因数中含有2.那么我们考虑会出现多少个2。可以联想求解n!末尾有多少个0,就会知道这里应该是

(N / 2) * (M / 2) + (N / 4) * (M /4) + ... + (N / 2k) * (M / 2k)  (2k <= min(N, M))

打素数的表, 然后求解每个素数的指数,快速幂取模

#include <cstdio>
#define MOD 1000000007
using namespace std;
typedef long long LL;

int N, M;
const int maxn = 10000000 + 10;
bool is_prime[maxn];
int prime[1000000];

void get_prime() {
    for (int i = 0; i < maxn; i++) is_prime[i] = true;
    is_prime[0] = is_prime[1] = false;
    for (int i = 2; i < maxn; i++) {
        if (is_prime[i]) {
            prime[++prime[0]] = i;
        }
        for (int j = i + i; j < maxn; j += i) is_prime[j] = false;
    }
}

LL qpow(LL x, LL n) {
    LL res = 1;
    while (n > 0) {
        if (n & 1) res = res * x % MOD;
        x = x * x % MOD;
        n >>= 1;
    }
    return res;
}


void solve() {
    LL ans = 1;
    int small = min(N, M);
    for (LL i = 1; i <= prime[0] && prime[i] <= small; i++) {
        LL num = prime[i];
        LL tmp = 0;
        while (num <= small) {
            tmp += (LL)(N / num) * (M / num);
            num *= prime[i];
            //digit++;
        }
        ans *= qpow(prime[i], tmp);
        ans = ans % MOD;
    }
    printf("%lld
", ans);
}

int main(int argc, const char * argv[]) {
    get_prime();
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &N, &M);
        solve();
        
    }
    return 0;
}
原文地址:https://www.cnblogs.com/xFANx/p/7266204.html