[SCOI2016] 妖怪 题解
\(\text{[SCOI2016] 妖怪 题解}\)
记两个参数分别为 \(p,q\),那么答案显然为 \(\dfrac{a+b}{a}p+\dfrac{a+b}{b}q\)。求出 \(a,b\) 是困难的,但是我们发现这个式子的取值实际上只和 \(\dfrac{b}{a}\) 有关,于是我们设 \(x=\dfrac ba\)。
由于是多个蝴蝶去最大值,我们不妨考虑相较之下取到最大值的情形。那么对于 \(i,j\),\(i\) 比 \(j\) 优当且仅当 \(\dfrac{a+b}{a}p_i+\dfrac{a+b}{b}q_i>\dfrac{a+b}{a}p_j+\dfrac{a+b}{b}q_j\),化简一些可以得到的是:
\[\frac{q_i-q_j}{p_i-p_j}>-x
\]
这是一个上凸包的情形,考虑一下在每个区间内在哪里取得最小值。这个相关的式子显然可以化成:\(xp+\dfrac 1xq\),求导得到的是 \(p-\dfrac{q}{x^2}\),那么当 \(x=\sqrt{\dfrac qp}\) 时取得最小值。考虑到这是一个单峰函数的形式,当 \(x\) 不在当前 \(k\) 的取值范围以内的时候一定是在 \(l,r\) 两个端点取得最值。需要留意的是有横纵坐标相同的点时需要把较小的那个点直接排除掉,不难发现这样做是正确的。
代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
using db = double;
const int N = 1e6 + 5;
const db inf = 1e9;
int n;
struct PNT {
int x, y;
bool operator < (const PNT &b) const {
if (x == b.x) return y > b.y;
return x < b.x;
}
PNT operator - (PNT b) {
return {x - b.x, y - b.y};
}
ll operator * (PNT b) {
return 1ll * x * b.y - 1ll * y * b.x;
}
};
vector<PNT>v, t;
PNT stk[N];
int top;
db k[N];
void out(char c, db x) {
cout << c << ' ' << fixed << ' ' << setprecision(9) << x << '\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
int x, y;
cin >> x >> y;
v.emplace_back((PNT){x, y});
}
sort(v.begin(), v.end());
for (auto i : v) {
if (t.size() && t.back().x == i.x) continue;
t.emplace_back(i);
}
for (auto &i : t) swap(i.x, i.y);
v.clear();
sort(t.begin(), t.end());
for (auto i : t) {
if (v.size() && v.back().x == i.x) continue;
v.emplace_back(i);
}
for (auto &i : v) swap(i.x, i.y);
sort(v.begin(), v.end());
for (auto i : v) {
while (top > 1 && (i - stk[top]) * (stk[top] - stk[top - 1]) <= 0) --top;
stk[++top] = i;
}
vector<PNT>().swap(v);
vector<PNT>().swap(t);
k[1] = inf;
for (int i = 2; i <= top; i++)
k[i] = 1.0 * (stk[i].y - stk[i - 1].y) / (stk[i].x - stk[i - 1].x);
k[top + 1] = -inf;
db ans = inf;
for (int i = 1; i <= top; i++) {
db l = k[i + 1], r = k[i];
if (l >= 0) continue;
db p = stk[i].x, q = stk[i].y, x = sqrt(q / p), vl = 0;
if (r >= 0) r = -1e-6;
if (-x < l || -x > r) vl = min(-l * p + q / -l, -r * p + q / -r);
else vl = x * p + q / x;
ans = min(ans, p + q + vl);
}
cout << fixed << setprecision(4) << ans << '\n';
return 0;
}

浙公网安备 33010602011771号