[BZOJ4570][Scoi2016]妖怪(凸包+数学分析)

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=4570

Solution

显然,对于攻击力为 A ,防御力为 B 的妖怪,在环境 ( a , b ) 下的战斗力为:

A + a b B + B + b a A

可以发现真正与妖怪战斗力有关的是 b a 而不是 ( a , b ) 。 设 x = b a
因此原式化为:
A + 1 x B + B + A x = ( 1 + x ) A + 1 + x x B

考虑两只妖怪,第一只的攻击力和防御力分别为 A , B ,第二只的攻击力和防御力分别为 C , D A > C ),那么第一只妖怪的战斗力比第二只强,当且仅当:
( 1 + x ) A + 1 + x x B > ( 1 + x ) C + 1 + x x D

( 1 + x ) A ( 1 + x ) C > 1 + x x D 1 + x x B

D B A C < x

B D A C > x

左边是斜率的表示!
将每个妖怪看作一个点 ( a t k , d n f )
如果你学过斜率优化或其他相关的东西,那么你可以看出:
所有战斗力可能最大的妖怪都在所有点组成的 上凸壳上。
P i 上凸壳上从左到右第 i 个点, s l o p e ( X , Y ) 为点 X 和点 Y 所在直线的斜率,
那么 P i 为战斗力最大值,当且仅当:
s l o p e ( P i + 1 , P i ) x s l o p e ( P i , P i 1 )

也就是说:
s l o p e ( P i , P i 1 ) x s l o p e ( P i + 1 , P i )

也就是说,将 正数 x 定义在一个区间 [ l , r ] 内,求 x 取何值时 A x + B x 最小 ,并求最小值。
显然,如果对 x 的值的限制只是正数,那么 x = A B A 时,上式取得最小值 2 A B
因此可以分类讨论:
(1) r < A B A 时, x r
(2) l > A B A 时, x l
(3) l A B A r 时, x A B A
特殊:如果 P i 是上凸壳的第一个或最后一个点,那么 x 就没有上界限制或没有下界限制(不过 x 必须大于 0 )。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
typedef long long ll; const int N = 1e6 + 5;
int n, top; double ans = 1e133;
struct cyx {
    int x, y;
    inline cyx operator - (cyx rhs) {return (cyx) {rhs.x - x, rhs.y - y};}
    inline ll operator * (cyx rhs) {return 1ll * x * rhs.y - 1ll * y * rhs.x;}
    inline bool operator < (cyx rhs) {
        return x < rhs.x || (x == rhs.x && y > rhs.y);
    }
    inline double slope(cyx rhs) {
        return 1.0 * (y - rhs.y) / (x - rhs.x);
    }
} a[N], stk[N];
int main() {
    int i; n = read(); For (i, 1, n) a[i].x = read(), a[i].y = read();
    sort(a + 1, a + n + 1); For (i, 1, n) {
        if (i > 1 && a[i - 1].x == a[i].x) continue;
        while (top > 1 && (stk[top - 1] - stk[top]) * (stk[top - 1] - a[i]) > 0)
            top--; stk[++top] = a[i];
    }
    For (i, 1, top) {
        double l = 0, r = 1e79;
        if (i > 1) l = max(l, -stk[i - 1].slope(stk[i]));
        if (i < top) r = min(r, -stk[i].slope(stk[i + 1])); if (l > r) continue;
        double mid = sqrt(1.0 * stk[i].x * stk[i].y) / stk[i].x;
        if (r < mid) ans = min(ans,
            r * stk[i].x + 1.0 * stk[i].y / r + stk[i].x + stk[i].y);
        else if (l > mid) ans = min(ans,
            l * stk[i].x + 1.0 * stk[i].y / l + stk[i].x + stk[i].y);
        else ans = min(ans, mid * stk[i].x + 1.0 * stk[i].y / mid
            + stk[i].x + stk[i].y);
    }
    printf("%.4lf\n", ans); return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/80933884