Luogu P11014 「ALFR Round 4」D 罪人的终幕 题解

题目定义的函数 \(a\) 更常见的记法为 \(\Omega\)OEIS A008472,容易说明其为加性函数),后文亦使用 \(\Omega\)

为了好看,后文用 \(a_i\) 代指题目中的 \(m_i\)

题意即转化为:

给定整数 \(n, a_1, k\) 和长为 \(n\) 的整数序列 \(w\),设

\[a_i = \max\limits_{1 \leqslant j < i} \left\{ \frac {a_j} {\Omega(\operatorname{lcm}(w_i, w_j)) + \Omega(\gcd(w_i, w_j))} \right\} + k \qquad 2 \leqslant i \leqslant n \]

\[\sum\limits_{i = 1}^n a_i \]

\(1 \leqslant n \leqslant 182376, 2 \leqslant w_i \leqslant 182376\)

\(2 \leqslant i \leqslant n\),由(因为 \(xy\)\(\operatorname{lcm}(x, y)\) 的质因子相同)

\[\Omega(xy) = \Omega(\operatorname{lcm}(x, y)) \]

\[a_i = \max\limits_{1 \leqslant j < i} \left\{ \frac {a_j} {\Omega(w_i w_j) + \Omega(\gcd(w_i, w_j))} \right\} + k \]

由(容斥质因子可得)

\[\Omega(xy) = \Omega(x) + \Omega(y) - \Omega(\gcd(x, y)) \]

\[\begin{align*} a_i &= \max\limits_{1 \leqslant j < i} \left\{ \frac {a_j} {\Omega(w_i) + \Omega(w_j) - \Omega(\gcd(w_i, w_j)) + \Omega(\gcd(w_i, w_j))} \right\} + k \\ &= \max\limits_{1 \leqslant j < i} \left\{ \frac {a_j} {\Omega(w_i) + \Omega(w_j)} \right\} + k \\ \end{align*} \]

发现 \(\Omega(w_i)\) 是定值,但位置在分母,通过取倒数让它移动到分子,即

\[\begin{align*} a_i &= \max\limits_{1 \leqslant j < i} \left\{ \frac {a_j} {\Omega(w_i) + \Omega(w_j)} \right\} + k \\ &= \frac 1 {\min\limits_{1 \leqslant j < i} \left\{ \frac {\Omega(w_i) + \Omega(w_j)} {a_j} \right\}} + k \\ \end{align*} \]

考虑求

\[\min\limits_{1 \leqslant j < i} \left\{ \frac {\Omega(w_i) + \Omega(w_j)} {a_j} \right\} \]

发现这可以看作关于 \(\Omega(w_i)\) 的一次函数,斜率为 \(\frac 1 {a_j}\),截距为 \(\frac {\Omega(w_j)} {a_j}\)

因为 \(m_i\) 一定是正数,所以可能成为答案的一次函数的图像构成一个上凸包,李超线段树或者平衡树维护凸包即可。

时间复杂度 \(O(n \log w)\)\(w\) 为值域。

基于李超线段树的参考实现如下,注意本题对精度要求较高。

#include <iomanip>
#include <iostream>

using namespace std;

typedef long double ld;

const int lim = 500 * 365;
const ld inf = 1e18;
const ld eps = 1e-15;

bool vis[lim + 5];
int pr[lim + 5], tail;
int Omega[lim + 5];

int n, k;
int w[lim];
ld a[lim];

struct line {
    ld k, b;
    inline ld get(int x) const { return k * x + b; }
} d[800005];
static inline void build(int s, int t, int p) {
    d[p].b = inf;
    if (s == t)
        return;
    int mid = (s + t) >> 1;
    build(s, mid, p << 1);
    build(mid + 1, t, p << 1 | 1);
}
static inline void insert(line nd, int s, int t, int p) {
    int mid = (s + t) >> 1;
    line l1 = nd;
    line &l2 = d[p];
    if (l1.get(mid) - l2.get(mid) < -eps)
        swap(l1, l2);
    if (l1.get(s) - l2.get(s) < -eps)
        insert(l1, s, mid, p << 1);
    if (l1.get(t) - l2.get(t) < -eps)
        insert(l1, mid + 1, t, p << 1 | 1);
}
static inline ld query(int x, int s, int t, int p) {
    ld ret = d[p].get(x);
    if (s == t)
        return ret;
    int mid = (s + t) >> 1;
    if (x <= mid)
        return min(ret, query(x, s, mid, p << 1));
    else
        return min(ret, query(x, mid + 1, t, p << 1 | 1));
}

static inline line make_line(int x) { return {1. / a[x], Omega[w[x]] / a[x]}; }

static inline void solve() {
    cin >> n >> a[1] >> k;
    for (int i = 1; i <= n; ++i)
        cin >> w[i];
    build(0, 2e5, 1);
    insert(make_line(1), 0, 2e5, 1);
    for (int i = 2; i <= n; ++i) {
        ld inv = query(Omega[w[i]], 0, 2e5, 1);
        a[i] = 1. / inv + k;
        insert(make_line(i), 0, 2e5, 1);
    }
    ld ans = 0;
    for (int i = 1; i <= n; ++i)
        ans += a[i];
    cout << fixed << setprecision(12) << ans << endl;
}

signed main() {
#ifndef ONLINE_JUDGE
    freopen("P11014.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    Omega[1] = 0;
    for (int i = 2; i <= lim; ++i) {
        if (!vis[i]) {
            pr[++tail] = i;
            Omega[i] = i;
        }
        for (int j = 1; j <= tail && i * pr[j] <= lim; ++j) {
            vis[i * pr[j]] = 1;
            if (i % pr[j] == 0) {
                Omega[i * pr[j]] = Omega[i];
                break;
            }
            Omega[i * pr[j]] = Omega[i] + pr[j];
        }
    }
    solve();
    return 0;
}
posted @ 2024-08-31 01:04  bluewindde  阅读(12)  评论(0编辑  收藏  举报