爱思创 181021 gcd区间 题解

181021 gcd区间

思路

这一道题我们要先知道什么是 最大公因数 (gcd)

最大公因数,也称最大公约数、最大公因子,指两个或多个整数共有约数中最大的一个。

在程序中可以直接调用函数: __gcd()
不过我们还是用辗转相除法手写一个递归版吧。

int gcd(int a, int b)
{
    return b == 0 ? a : gcd(b, a % b);
}

求多个数的最大公因数一直gcd就行,比如 \(gcd(a, b, c, d) = gcd(gcd(gcd(a, b), c), d)\)
现在可以求最大公因数了,开始想其他部分

这一题要求很多个区间的最大公因数,是一道 RMQ 问题 的变种,正常枚举肯定不行,需要优化。我们可以用 ST表 来做
ST表类似动态规划(dp),基于 倍增 思想,它能够使线性的处理转化为对数级的处理,大大地优化时间复杂度。
st[i][j]意为从第 \(i\) 位开始的 \(2^j\) 位的最大公因数

状态转移方程式为

st[i][j] = gcd(st[i][j - 1]/*左区间*/, st[i + (1 << (j - 1))][j - 1]/*右区间*/);
//两个区间用gcd()合并

输出时

int len = r - l + 1;//区间总长
int k = log2(len);
printf("%d\n", gcd(st[l][k]/*左覆盖区间*/, st[r - (1 << k) + 1/*右区间-长度+1为右覆盖区间起点*/][k]/*右覆盖区间*/)/*整体gcd()*/);
整体代码(上面没讲的细节看注释)
#include<bits/stdc++.h>
using namespace std;
int st[1005][15/*大于log2(1000)即可*/];
int a[1005];
int gcd(int a, int b)
{
    return b == 0 ? a : gcd(b, a % b);
}
int main()
{
    int n, m;
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
    for(int i = 1; i <= n; i ++) st[i][0] = a[i];
    for(int j = 1; (1 << j) <= n; j ++)//外层循环的是j(长度),不是i(位置)
    {
        for(int i = 1; i + (1 << (j - 1))/*处理溢出*/ <= n; i ++)
        {
            st[i][j] = gcd(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
        }
    }
    while(m --)
    {
        int l, r;
        scanf("%d %d", &l, &r);
        int len = r - l + 1;
        int k = log2(len);
        printf("%d\n", gcd(st[l][k], st[r - (1 << k) + 1][k]));
    }
    return 0;
}
posted @ 2022-12-03 16:30  yuzihang  阅读(45)  评论(0)    收藏  举报