Loading

洛谷 P3291 [SCOI2016] 妖怪

设每只怪物经过环境影响后的攻击力和防守力分别为 \(x_i, y_i\),则有:

\(y_i = dnf_i - \dfrac ba(x_i -atk_i)\)

\(k = -\dfrac ba\),则有 \(y_i= kx_i + dnf_i - k \cdot atk_i\)

设直线 \(l_i : y_i= kx_i + dnf_i - k \cdot atk_i\),第 \(i\) 只怪物在 \((a, b)\) 的环境下的战斗力为 \(f_i(k)\),则有:

\[f_i(k) = \max\{y_i\} + \max\{x_i\} = atk_i + dnf_i - k \cdot atk_i - \dfrac{dnf_i}k \]

再来探究 \(f_i(k)\) 的实质:不难发现是 \(l_i\) 的纵横截距之和。

\(l_i\) 是什么呢?

回到我们最初的式子:\(y_i= kx_i + dnf_i - k \cdot atk_i\),此时把 \(dnf_i\) 移到右侧,再在右侧提个 \(k\) 出来,就恰巧构成了一个一次函数的点斜式,即:

\[y_i - dnf_i = k(x_i - atk_i) \]

所以 \(l_i\) 就是一条过点 \((atk_i, dnf_i)\) 的斜率为 \(k\) 的直线。

设直线 \(l : y = kx + b(b \in \R)\)\(V = \{(atk_i, dnf_i) | i \in [1, n]\}\) 的上凸包相切,那么,\(\max\{f_i(k)\}\) 也就可以理解为 \(l\) 的纵横截距之和。

接下来考虑对于每个单独的点 \((atk_i, dnf_i)\),何时其为 \(l\)\(V\) 的上凸包的切点。

\(k_1, k_2\) 分别为凸包上第 \(i - 1\) 个点与第 \(i\) 个点、第 \(i\) 个点与第 \(i + 1\) 个点的斜率,则当且仅当 \(k \in [k_2, k_1]\) 时,\(f_i(k)\) 为切点。

再考虑 \(f_i(k)\) 何时取得最小值,由均值不等式得当且仅当 \(k = -\sqrt{\dfrac{dnf_i}{atk_i}}\) 时,\(f_i(k)\) 取得最小值。

所以,计算答案的方式如下:

  • \(-\sqrt{\dfrac{dnf_i}{atk_i}} \in [k_2, k_1]\),则 \(ans = \min\{ans, f_i(-\sqrt{\dfrac{dnf_i}{atk_i}})\}\)
  • 否则,\(ans = \min\{ans, f_i(k_2), f_i(k_1)\}\)

时间复杂度 \(O(n \log n)\)

代码是目前(2023/7/25)的最优解

#include <bits/stdc++.h>

#define MAXN 1000100

using namespace std;

int n;
int top, stk[MAXN];

struct Node {
    int atk, dnf;
    double mink;

    double f(double k) {
        return atk + dnf - atk * k - dnf / k;
    }

    Node operator-(const Node &rhs) const {
        return {atk - rhs.atk, dnf - rhs.dnf};
    }

    long long operator^(const Node &rhs) const {
        return 1ll * atk * rhs.dnf - 1ll * rhs.atk * dnf;
    }

    bool operator<(const Node &rhs) const {
        if (atk == rhs.atk) return dnf > rhs.dnf;
        return atk < rhs.atk;
    }
} p[MAXN];

double getk(Node x, Node y) {
    return 1.0 * (x.dnf - y.dnf) / (x.atk - y.atk);
}

template<typename _T>
void read(_T &_x) {
    _x = 0;
    _T _f = 1;
    char _ch = getchar();
    while (_ch < '0' || '9' < _ch) {
        if (_ch == '-') _f = -1;
        _ch = getchar();
    }
    while ('0' <= _ch && _ch <= '9') {
        _x = (_x << 3) + (_x << 1) + (_ch & 15);
        _ch = getchar();
    }
    _x  *= _f;
}

int main() {
    read(n);
    for (int i = 1; i <= n; i++) {
        read(p[i].atk), read(p[i].dnf);
        p[i].mink = -sqrt(1.0 * p[i].dnf / p[i].atk);
    }
    sort(p + 1, p + n + 1);
    for (int i = 1; i <= n; i++) {
        while (top > 1 && ((p[i] - p[stk[top - 1]]) ^ (p[stk[top]] - p[stk[top - 1]])) <= 0) top--;
        stk[++top] = i;
    }
    double ans = min(p[stk[top]].f(min(p[stk[top]].mink, getk(p[stk[top - 1]], p[stk[top]]))), p[stk[1]].f(max(p[stk[1]].mink, getk(p[stk[1]], p[stk[2]]))));
    for (int i = 2; i < top; i++) {
        double k1 = getk(p[stk[i - 1]], p[stk[i]]), k2 = getk(p[stk[i]], p[stk[i + 1]]);
        if (k2 <= p[stk[i]].mink && p[stk[i]].mink <= k1) ans = min(ans, p[stk[i]].f(p[stk[i]].mink));
        else ans = min(ans, min(p[stk[i]].f(k2), p[stk[i]].f(k1)));
    }
    printf("%.4lf", ans);
    return 0;
}
posted @ 2023-07-25 14:42  Chy12321  阅读(21)  评论(0编辑  收藏  举报