首页 > 其他分享 >洛谷 P3291 [SCOI2016] 妖怪

洛谷 P3291 [SCOI2016] 妖怪

时间:2023-07-25 15:12:47浏览次数:56  
标签:Node dnf 洛谷 dfrac rhs P3291 atk return 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;
}

标签:Node,dnf,洛谷,dfrac,rhs,P3291,atk,return,SCOI2016
From: https://www.cnblogs.com/chy12321/p/17579902.html

相关文章

  • 洛谷P3629 [APIO2010] 巡逻题解
    题目链接P3629[APIO2010]巡逻-洛谷|计算机科学教育新生态(luogu.com.cn)思路n个村庄,n-1条道路,原图为树1.若k=0(不修建道路)那么答案为(n-1)*2 每个道路会走两遍2.若k为1(修建一条道路)设修建的道路(r1)所在的环长度为L那么答案为(n-1)*2-L+2可以看到r1所在环的道路只走了......
  • 洛谷CF1738C题解
    好一道博弈论水题题目传送门更好的食用体验题目大意:给定长度为$n$的数列$a_1,a_2,\dots,a_n$,两名玩家Alice、Bob依次以最优策略从数列中取走一个数,Alice先取,直至为空博弈结束。若Alice取走的所有数之和为偶,Alice胜利;若Alice取走的所有数之和为奇,Bob胜利......
  • 洛谷 T356695 文字处理软件(重置版)
    很简单了啊!说普及-我都不信作者(也就是我)链接:https://www.luogu.com.cn/problem/T356695好好想想!!!!题目!文字处理软件(重置版)题目背景Allow是一名程序员,他要为公司开发一款“文字处理软件”!题目描述用户可能输入∞个数字。说白了用while(1)输入1时,把字符串原样输出。......
  • 洛谷AT_jsc2019_qual_e Card Collector 题解
    题目链接CardCollector-洛谷|计算机科学教育新生态(luogu.com.cn)思路将每一行、每一列转化为点,第i行第j列的卡牌转化为i->j+m(m为行数)的有向边。总共会抽取m+n(m为行数,n为列数)张牌,每个点的出度为1。结果图为基环森林;那么题目就转化为求最大基环森林。代码1#include......
  • 洛谷3294 背单词
    这题乍一看是排序贪心,然后使用领项交换来做题由于有了第一条规则的存在,因为\(n*n\)远大于另外两条规则所产生的代价,所以我们不会让后缀排在后面于是乎,我们倒序建立trie树并且重构树(具体可见洛谷题解),那么问题就转换为给这棵树标号,要求必须标了父亲才能标儿子,令每一条边的代价为......
  • 洛谷 P8490 [IOI2022] 鲶鱼塘
    洛谷传送门LOJ传送门不算很难的题,但是调起来比较恶心。下文默认下标从\(1\)开始。设第\(i\)列长堤的高度为\(h_i\)。考虑观察一些性质。Observation1:若\(h_{i-1}<h_i>h_{i+1}\),那么\(h_i=n\)一定不劣。若\(h_i<n\),\([h_i+1,n]\)的鱼是抓不到的,并......
  • 洛谷 P8861 - 线段
    牛逼题。先考虑\(l\le10^5,10^5+1\ler\)的部分分:一种方法是线段树,即因为左右端点是独立的,因此对左右端点各维护一个权值线段树表示有多少个区间以这个值为左/右端点,这样对于修改,左端点的部分相当于先查询\(\lel\)的数的个数,然后将它们都挂到\(l\)上,最后把\(<l\)的部......
  • 洛谷 P9139 [THUPC 2023 初赛] - 喵了个喵 II
    考虑如果每个数恰好出现两次,那么容易得出一个序列合法当且仅当将每个数两次出现位置看作一个区间\([l_i,r_i]\)的两个端点,那么这些区间两两之间不存在包含关系。考虑每个数出现四次的情况,我们钦定两次为\(i\),两次为\(i+n\),这样可以转化为\(2n\)的情况,而容易发现只有\(1122......
  • 再见,洛谷博客!——下载洛谷博客文章
    postedon2022-11-0610:58:17|under学术|source前置知识单组询问F12//copythecodeofblogsfetch('/api/blog/detail/'+BlogGlobals.blogID).then(res=>res.json()).then(res=>console.log(res.data.Content))多次询问下载markdown#fetcher.pyimpo......
  • 洛谷 P9020 - [USACO23JAN] Mana Collection P
    显然,每个法力池最终能收集到的法力只与这个法力池最终被收集到的时间有关。对于一组询问\((s,e)\),假设我们经过了\(k\)个法力池,我们钦定最终被收集到的时间从后到前分别是\(e=a_1,a_2,\cdots,a_k\),那么最大法力值为\(\sum\limits_{i=1}^kc_{a_i}·\sum\limits_{j=2}^i(s-dis......