您的位置:首页 > 其它

[BZOJ]4570 [SCOI2016] 妖怪 凸包 + 双钩函数求最值

2017-12-05 10:56 288 查看

4570: [Scoi2016]妖怪

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 1025  Solved: 288

[Submit][Status][Discuss]

Description

邱老师是妖怪爱好者,他有n只妖怪,每只妖怪有攻击力atk和防御力dnf两种属性。邱老师立志成为妖怪大师,于
是他从真新镇出发,踏上未知的旅途,见识不同的风景。环境对妖怪的战斗力有很大影响,在某种环境中,妖怪可
以降低自己k×a点攻击力,提升k×b点防御力或者,提升自己k×a点攻击力,降低k×b点防御力,a,b属于正实数
,k为任意实数,但是atk和dnf必须始终非负。妖怪在环境(a,b)中的战斗力为妖怪在该种环境中能达到的最大攻击
力和最大防御力之和。strength(a,b)=max(atk(a,b))+max(dnf(a,b))环境由a,b两个参数定义,a,b的含义见前
文描述。比如当前环境a=3,b=2,那么攻击力为6,防御力为2的妖怪,能达到的最大攻击力为9,最大防御力为6。
所以该妖怪在a=3,b=2的环境下战斗力为15。因此,在不同的环境,战斗力最强的妖怪可能发生变化。作为一名优
秀的妖怪训练师,邱老师想发掘每一只妖怪的最大潜力,他想知道在最为不利的情况下,他的n只妖怪能够达到的
最强战斗力值,即存在一组正实数(a,b)使得n只妖怪在该环境下最强战斗力最低。

Input

第一行一个n,表示有n只妖怪。接下来n行,每行两个整数atk和dnf,表示妖怪的攻击力和防御力。
1≤n≤10^6, 0<atk,dnf≤10^8

Output

 输出在最不利情况下最强妖怪的战斗力值,保留4位小数。

Sample Input

3

1 1

1 2

2 2

Sample Output

8.0000

HINT

Source



[Submit][Status][Discuss]

HOME Back

  以前做的都是凸包裸题... 果然自己的算几水平还是弱爆了. 终于碰到关于凸包维护函数最值的了.

  那么把每个妖怪初始战斗力值看做是一个二维平面坐标系上的一个点, 那么这个妖怪的战斗力值就是过这个点的直线与x, y轴的截距之和. 首先很显然战斗力值是一个双钩函数(可以自己设斜率为k推一下) . 对于一个点r如果比另外一个点s的x以及y都要小的话, 那么显然不会对答案做出贡献, 因为r一定会被s所覆盖. 那么我们会发现只有凸包的右上半凸壳会对答案做出贡献. 那么求出这个凸壳上的点之后。 对每个点展开分类讨论. 每个点能取的得最小值, 因为是双钩函数,
所以均值不等式带入即可求出(初中就讲过...). 那么当前点的最小值的斜率如果能覆盖所有其他点的斜率, 那么这肯定是最大战斗力值, 那么更新答案. 如果如果覆盖不了, 也就是说其他点可能成为最大战斗力值. 那么我们根据双钩函数的单调性取到与上一个点的斜率来更新答案 -- 由于凸壳相邻点的斜率单调, 所以这样做一定能覆盖所有点来更新答案, 并且由于双钩函数的性质是当前最小能更新答案的斜率. 所以对于每个点更新答案的思路就是均值不等式求出的最优斜率能覆盖就直接更新答案, 否则用与上一点连成的斜率更新答案. 

  Upd: 数据怎么这么水啊之前写成-sqrt(r.y / r,y)都可以过... 代码已改正并AC.

#include<bits/stdc++.h>
#define pp pop_back
#define pb push_back
using namespace std;
int n, m;
double ans, minum, maxum;
struct Vector {
double x, y;
Vector() {}
Vector(double x, double y) : x(x), y(y) {}
inline friend bool operator < (const Vector &r, const Vector &s) {
return r.x < s.x || (r.x == s.x && r.y > s.y);
}
inline friend Vector operator - (const Vector &r, const Vector &s) {
return Vector(r.x - s.x, r.y - s.y);
}
};
typedef Vector Point;
vector<Point> pts, cvx;
inline double cross(const Vector &r, const Vector &s) {
return r.x * s.y - r.y * s.x;
}
inline bool onleft(const Point &A, const Point &B, const Point &C) {
return cross(B - A, C - A) > 0;
}
inline double Slopego(const Point &r, const Point &s) {
return (s.y - r.y) / (s.x - r.x);
}
inline double bestK(const Point &r) {
return - sqrt(r.y / r.x);
}
inline double calc(const Point &r, double k) {
return r.x + r.y - k * r.x - (1.0 / k) * r.y;
}
int main() {
ans = 1ll << 61;
scanf("%d", &n);
for (int i = 1; i <= n; ++ i) {
double x, y;
scanf("%lf%lf", &x, &y);
pts.pb(Point(x, y));
}
sort(pts.begin(), pts.end());
for (int i = 0; i < n; ++ i) {
while (m > 1 && onleft(cvx[m - 2], cvx[m - 1], pts[i])) cvx.pp(), m --;
cvx.pb(pts[i]), m ++;
}
if (m == 1) {
ans = calc(cvx[0], bestK(cvx[0]));
} else {
double k;
for (int i = 0; i < m; ++ i) {
if (i < m - 1) minum = Slopego(cvx[i], cvx[i + 1]);
else minum = - (1ll << 61);
k = bestK(cvx[i]);
if (k >= minum && k <= maxum) ans = min(ans, calc(cvx[i], k));
if (i && cvx[i].y != cvx[i - 1].y) ans = min(ans, calc(cvx[i], Slopego(cvx[i - 1], cvx[i])));
maxum = minum;
}
}
printf("%.4f\n", ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: