4570: [Scoi2016]妖怪
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 1025 Solved: 288
[ Submit][ Status][ Discuss]
Description
Input
Output
输出在最不利情况下最强妖怪的战斗力值,保留4位小数。
Sample Input
1 1
1 2
2 2
Sample Output
HINT
Source
以前做的都是凸包裸题... 果然自己的算几水平还是弱爆了. 终于碰到关于凸包维护函数最值的了.
那么把每个妖怪初始战斗力值看做是一个二维平面坐标系上的一个点, 那么这个妖怪的战斗力值就是过这个点的直线与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);
}