跳转至

区间gcd

多次查询区间查询

\(\gcd(a_L,a_{L+1},...,a_R)\)

使用 ST表 可以 \(O(n*\log n)\) 预处理出区间的gcd,然后 \(O(1)\) 查询。

性质

\(f(L,R)=\gcd(a_L,a_{L+1},...,a_R)\)

\(f(L,L),f(L,L+1),f(L,L+2),...,f(L,R)\),一定是单调不升的一个序列。

从上可得,最多有 \(logx\)\(\gcd\) 的取值。

故可以通过 \(O(n*\log n)\) 的复杂度预处理出所有的区间 \(\gcd\)

例题

F-小辰刚学 gcd_牛客小白月赛81

多次查询集合 \(S=U_{i=L}^R \{\gcd_{j=i}^Ra_j\}\) 的大小

代码
C++
#include <bits/stdc++.h>
using namespace std;
// 2024 OneWan
long long a[600005];
vector<pair<long long, int>> f[600005];
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int n, m;
    cin >> n >> m;
    for (int i = 1 ; i <= n ; i++) {
        cin >> a[i];
    }
    for (int i = 1 ; i <= n ; i++) {
        long long g = a[i];
        f[i].emplace_back(g, i);
        for (auto &[preg, idx] : f[i - 1]) {
            long long tg = __gcd(g, preg);
            if (tg != g) {
                f[i].emplace_back(tg, idx);
                g = tg;
            }
            if (g == 1) break;
        }
    }
    while (m--) {
        int L, R;
        cin >> L >> R;
        int ans = 0;
        for (int i = 0 ; i < int(f[R].size()) ; i++) {
            // 此时区间 [L, R] 的 gcd 均为 f[R][i].first
            // L 属于区间 [f[R][i + 1].second + 1, f[R][i].second]
            // 当 i = f[R].size() - 1 时, L = 1
            if (L <= f[R][i].second) {
                ans++;
            }
        }
        cout << ans << "\n";
    }
    return 0;
}

K-同学聚会_第十一届"图灵杯"NEUQ-ACM程序设计竞赛

\(\dfrac{\sum_{L=1}^{n}\sum_{R=L}^n(R-L+1)*\gcd_{i=L}^Ra_i}{\dfrac{n*(n+1)}{2}}\)

代码
C++
#include <bits/stdc++.h>
using namespace std;
// 2024 OneWan
int a[200005];
vector<pair<int, int>> f[200005];
const long long mod = 998244353;
long long qpow(long long a, long long b) {
    long long res = 1;
    while (b) {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    for (int i = 1 ; i <= n ; i++) {
        cin >> a[i];
    }
    for (int i = n ; i >= 1 ; i--) {
        int g = a[i];
        f[i].emplace_back(g, i);
        for (auto &[preg, idx] : f[i + 1]) {
            int tg = __gcd(g, preg);
            if (tg != g) {
                f[i].emplace_back(tg, idx);
                g = tg;
            }
            if (g == 1) break;
        }
    }
    long long ans = 0;
    for (int i = 1 ; i <= n ; i++) {
        for (int j = 0 ; j < int(f[i].size()) ; j++) {
            // 此时区间 [i, R] 的 gcd 均为 f[i][j].first
            // R 属于区间 [f[i][j].second, f[i][j + 1].second - 1]
            // 当 j = f[i].size() - 1 时, R = n;
            int L = f[i][j].second, R, g = f[i][j].first;
            if (j < int(f[i].size()) - 1) {
                R = f[i][j + 1].second - 1;
            } else {
                R = n;
            }
            long long res = (1LL * (L + R) * (R - L + 1) / 2 - 1LL * (i - 1) * (R - L + 1) % mod + mod) % mod * g % mod;
            ans = (ans + res) % mod;
        }
    }
    cout << ans * qpow(1LL * n * (n + 1) / 2 % mod, mod - 2) % mod << "\n";
    return 0;
}