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;
}