[SP32079] ADAGF - Ada and Greenflies

Ada and Greenflies の 传送门

\(\forall i \in \left [ 1, n \right ]\),令当前左端点为 \(i\),右端点为 \(j\)

不断执行以下操作:

  1. 通过倍增找到 \(j\) 后面最后一个 \(k\),使得 \(\gcd(a_{i \sim j}) = \gcd(a_{i \sim k})\)

  2. 计算在区间 \(\left [ j, k \right ]\) 中的右端点的贡献;

  3. \(j\) 改为 \(k+1\) 表示下一种 gcd 的取值。

因为 gcd 至多有 \(\log\) 种不同的取值,加上倍增与 ST 表求区间 gcd,总共时间复杂度为 \(O(n \log^3 n)\)

甚至是纯 C

#include <stdio.h>
#define int long long
#define N 300005
int n, a[N];
int pw[N], GCD[19][N];
int gcd(int a, int b) { return !b ? a : gcd(b, a % b); }
static inline int get_gcd(int l, int r)
{
    int lg = pw[r - l + 1];
    return gcd(GCD[lg][l], GCD[lg][r - (1 << lg) + 1]);
}
signed main()
{
    scanf("%lld", &n);
    for (int i = 1; i <= n; ++i)
        scanf("%lld", a + i), GCD[0][i] = a[i];
    pw[1] = 0;
    for (int i = 2; i <= n; ++i)
        pw[i] = pw[i >> 1] + 1;
    for (int j = 1; j < 19; ++j)
        for (int i = 1; i + (1 << j) - 1 <= n; ++i)
            GCD[j][i] = gcd(GCD[j - 1][i], GCD[j - 1][i + (1 << j - 1)]);
    int ans = 0;
    for (int i = 1; i <= n; ++i)
    {
        int j = i;
        while (j <= n)
        {
            int tj = j, gcd = get_gcd(i, j);
            for (int k = 18; ~k; --k)
                if (tj + (1 << k) <= n && gcd == get_gcd(i, tj + (1 << k)))
                    tj += 1 << k;
            ans += gcd * (tj - j + 1);
            j = tj + 1;
        }
    }
    printf("%lld", ans);
    return 0;
}
posted @ 2025-01-24 18:10  SilverLi  阅读(74)  评论(0)    收藏  举报