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

Address

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

Solution

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

A+abB+B+baA A + a b B + B + b a A

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

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

(1+x)A(1+x)C>1+xxD1+xxB ( 1 + x ) A − ( 1 + x ) C > 1 + x x D − 1 + x x B

DBAC<x D − B A − C < x

BDAC>x B − D A − C > − x

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

也就是说:
slope(Pi,Pi1)xslope(Pi+1,Pi) − s l o p e ( P i , P i − 1 ) ≤ x ≤ − s l o p e ( P i + 1 , P i )

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

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值