BZOJ 4570: [Scoi2016]妖怪 (三分 || 凸包)

题目: BZOJ传送门

题目:洛谷传送门

题意:

邱老师有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只妖怪在该环境下最强战斗力最低。
 
输入
第一行一个n,表示有n只妖怪。接下来n行,每行两个整数atk和dnf,表示妖怪的攻击力和防御力。
1≤n≤10^6, 0<atk,dnf≤10^8
 
输出
输出在最不利情况下最强妖怪的战斗力值,保留4位小数。
 
 
 
思路一:三分
这题非常卡精度卡常,太恶心了
三分一下 a/b,那么最大的攻击就是 ( 1 + a/b ) * atk,最大的防御就是 ( 1 + b/a ) * dnf
三分代码参考了 ->
 
不用快读就会超时,太恶心了。
#include <bits/stdc++.h>
#define LL long long
#define mem(i, j) memset(i, j, sizeof(i))
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define dep(i, j, k) for(int i = k; i >= j; i--)
#define pb push_back
#define make make_pair
#define INF INT_MAX
#define inf LLONG_MAX
#define PI acos(-1)
#define fir first
#define sec second
using namespace std;

const int N = 1e6 + 5;
const double eps = 1e-8;

int a[N], b[N];

int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}

int dcmp(double x) {
    if(fabs(x) < eps) return 0; else return x < 0 ? -1 : 1;
}

double cal(double mid, int n) {
    double ma = 0.0;
    rep(i, 1, n) {
        ma = max(ma, a[i] * (1 + mid) + b[i] * (1 + 1 / mid));
    }
    return ma;
}

int main() {
    int n;
    n = read();

    rep(i, 1, n) a[i] = read(), b[i] = read();

    double l = eps, r = 1 / eps;

    while(l + 1E-6 <= r) {

        double midl = (l + r) / 2.0 - eps;
        double midr = (l + r) / 2.0 + eps;

        if(cal(midl, n) < cal(midr, n)) {
            r = midr;
        }
        else {
            l = midl;
        }
    }

    rep(i, 1, 35) {
        double midl = l + (r - l) / 3.0;
        double midr = r - (r - l) / 3.0;
        if(cal(midl, n) < cal(midr, n)) r = midr;
        else l = midl;
    }

    printf("%.4f\n", cal(l, n));

    return 0;
}
View Code

 

 思路二:凸包

 思路参考:Go

代码参考:Go

 

 

 这里加一点自己的理解:

1、最大值在凸包的右上部分,因为这部分的 x 和 y 都是最大的。

2、这个点成为最大值的 k 的区间就是相邻两条线段的斜率之间部分,所以因为 k 若不在这部分,它不能成为最大值,所以不能用它来更新答案。

 

#include <bits/stdc++.h>
#define LL long long
#define mem(i, j) memset(i, j, sizeof(i))
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define dep(i, j, k) for(int i = k; i >= j; i--)
#define pb push_back
#define make make_pair
#define INF INT_MAX
#define inf LLONG_MAX
#define PI acos(-1)
#define fir first
#define sec second
using namespace std;

const int N = 1e6 + 5;
const double eps = 1e-8;

struct Point {
    double x, y;
    Point(double x = 0, double y = 0) : x(x), y(y) { }
};

int dcmp(double x) {
    if(fabs(x) < eps) return 0; else return x < 0 ? -1 : 1;
}

Point operator + (Point A, Point B) { return Point(A.x + B.x, A.y + B.y); }
Point operator - (Point A, Point B) { return Point(A.x - B.x, A.y - B.y); }
Point operator * (Point A, double p) { return Point(A.x * p, A.y * p); }
Point operator / (Point A, double p) { return Point(A.x / p, A.y / p); }
double Cross(Point A, Point B) { return A.x * B.y - A.y * B.x; }
bool operator < (Point A, Point B) { return A.x < B.x || (A.x == B.x && A.y < B.y); }

int ConvexHull(Point* p, int n, Point* ch) {
    sort(p, p + n);
    int m = 0;
    ch[++m] = p[0];
    rep(i, 1, n - 1) {
        while(m >= 0 && dcmp(Cross(ch[m] - ch[m - 1], p[i] - ch[m])) >= 0) m--;
        ch[++m] = p[i];
    }
    return m;
}

Point P[N], Q[N];

int main() {

    int n;
    scanf("%d", &n);
    rep(i, 0, n - 1) scanf("%lf %lf", &P[i].x, &P[i].y);

    n = ConvexHull(P, n, Q);

    double ans = 12345678900.0;

    rep(i, 1, n) {
        double nowk = -(sqrt(Q[i].y) / sqrt(Q[i].x));
        double prek = (i - 1 > 0 ? ((Q[i - 1].y - Q[i].y) / (Q[i - 1].x - Q[i].x)) : 0);
        double nxtk = (i + 1 <= n ? ((Q[i].y - Q[i + 1].y) / (Q[i].x - Q[i + 1].x)) : -12345678900.0);
        if(nowk > prek) ans = min(ans, Q[i].x * (1 - prek) + Q[i].y * (1 - 1/prek));
        else if(nowk < nxtk) ans = min(ans, Q[i].x * (1 - nxtk) + Q[i].y * (1 - 1/nxtk));
        else ans = min(ans, Q[i].x * (1 - nowk) + Q[i].y * (1 - 1/nowk));
    }

    printf("%.4f\n", ans);

    return 0;
}
View Code

 

posted on 2020-03-15 12:58  Willems  阅读(84)  评论(0编辑  收藏  举报

导航