[BZOJ4454][C Language Practice][O(1)GCD]

该博客介绍了如何在O(N)预处理后,实现O(1)时间复杂度求解最大公约数的方法。针对BZOJ4454问题,博主提出通过分解素数并预处理每个数的最小质因数来优化算法,以解决内存限制下的高效计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[BZOJ4454][C Language Practice][GCD]

题目大意:

这里写图片描述

思路:
  • 如果对于每两个数都求一次gcd显然会超时。

  • 考虑O(N)预处理,O(1)求gcd。

  • 不妨设m=n,可以在O(m2)也就是O(N)的时间里求出m范围内的gcd(辗转相除递推)

  • m范围外的数X,一定可以分解成ABC的形式,其中A,B,C要么<=m,要么是一个素数。

  • 分解素数可以线性筛预处理出每个数的最小质因数d,然后AX,BX,CX可以由AX/d,BX/d,CX/d转移得到

  • Hint

    • 数组要开的小心,因为内存范围限制得很紧

    • 大部分范围用int就可以,除了答案要用unsigned

    • 读入优化的缓存数组开太大会炸掉

代码:
#include <bits/stdc++.h>
using namespace std;
const int Maxn = 1000010;
inline void read(int &x) {
    scanf("%d", &x);
}
int g[1010][1010], p[Maxn / 10], tot, d[Maxn], n, m, A[Maxn], B[Maxn], C[Maxn];
bool vis[Maxn];
inline void init(int n) {
    for (int i = 0; i <= 1005; i++)
        for (int j = 0; j <= i; j++) {
            if (!i || !j) g[i][j] = i + j;
            else g[i][j] = g[j][i % j];
        }
    for (int i = 0; i <= 1005; i++)
        for (int j = i + 1; j <= 1005; j++) g[i][j] = g[j][i];
    d[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!vis[i]) {
            p[++tot] = i;
            d[i] = i;
        }
        for (int j = 1; j <= tot; j++) {
            if (i * p[j] > n) break;
            vis[i * p[j]] = 1;
            if (i % p[j] == 0) {
                d[i * p[j]] = d[i];
                break;
            }
            d[i * p[j]] = p[j];
        }
    }
    A[1] = B[1] = C[1] = 1;
    for (int i = 2; i <= n; i++) {
        int j = i / d[i];
        A[i] = A[j], B[i] = B[j], C[i] = C[j];
        if (A[i] * d[i] <= 1000) A[i] *= d[i];
        else if (B[i] * d[i] <= 1000) B[i] *= d[i];
        else C[i] *= d[i];
    }
}
int X[3], c;
inline int gcd(int a, int b) {
    if (!a || !b) return a + b;
    if (a <= 1000 && b <= 1000) return g[a][b];
    c = 0; int ans = 1, d = 1;
    if (A[a] != 1) X[c++] = A[a];
    if (B[a] != 1) X[c++] = B[a];
    if (C[a] != 1) X[c++] = C[a];
    for (int i = 0; i < c; i++) {
        if (X[i] <= 1000) d = g[X[i]][b % X[i]];
        else if (b % X[i] == 0) d = X[i];
        else d = 1;
        ans *= d; b /= d;
    }
    return ans;
}
int T, a[2010], b[2010];
int main(void) {
    //freopen("in.txt", "r", stdin);
    init(1000000);
    read(T);
    while (T--) {
        read(n), read(m);
        for (int i = 0; i < n; i++) read(a[i]);
        for (int i = 0; i < m; i++) read(b[i]);
        unsigned int ans = 0;
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
                ans += gcd(a[i], b[j]) ^ i ^ j;
        printf("%u\n", ans);
    }
    return 0;
}

完。

By g1n0st

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值