[Luogu P3291] [BZOJ 3570] [SCOI2016]妖怪

洛谷傳送門

BZOJ傳送門

題目描述

邱老師是妖怪愛好者,他有nn只妖怪,每隻妖怪有攻擊力atk和防禦力dnf兩種屬性。邱老師立志成爲妖怪大師,於是他從真新鎮出發,踏上未知的旅途,見識不同的風景。

環境對妖怪的戰鬥力有很大影響,在某種環境中,妖怪可以降低自己kak*a點攻擊力,提升kbk*b點防禦力或者,提升自己kak*a點攻擊力,降低kbk*b點防禦力,aabb屬於正實數,kk爲任意實數,但是atk和dnf必須始終非負。

妖怪在環境(a,b)(a,b)中的戰鬥力爲妖怪在該種環境中能達到的最大攻擊力和最大防禦力之和。strength(a,b)=max(atk(a,b))+max(dnf(a,b))strength(a,b)=max(atk(a,b))+max(dnf(a,b))環境由aabb兩個參數定義,aabb的含義見前文描述。

比如當前環境a=3a=3b=2b=2,那麼攻擊力爲66,防禦力爲22的妖怪,能達到的最大攻擊力爲99,最大防禦力爲66。所以該妖怪在a=3a=3b=2b=2的環境下戰鬥力爲1515

因此,在不同的環境,戰鬥力最強的妖怪可能發生變化。

作爲一名優秀的妖怪訓練師,邱老師想發掘每一隻妖怪的最大潛力,他想知道在最爲不利的情況下,他的nn只妖怪能夠達到的最強戰鬥力值,即存在一組正實數(a,b)(a,b)使得nn只妖怪在該環境下最強戰鬥力最低。

輸入輸出格式

輸入格式:

第一行一個nn,表示有nn只妖怪。

接下來nn行,每行兩個整數atk和dnf,表示妖怪的攻擊力和防禦力。1n106,0atk,dnf1081\le n\le 10^6, 0<atk, dnf\le 10^8

輸出格式:

輸出在最不利情況下最強妖怪的戰鬥力值,保留44位小數。

輸入輸出樣例

輸入樣例#1:

3
1 1
1 2
2 2

輸出樣例#1:

8.0000

解題分析

題目就是要我們求下面這個式子:
min(maxi=1n(x+y+bax+aby)) min(max_{i=1}^{n}(x+y+\frac{b}{a}x+\frac{a}{b}y))
k=bak=\frac{b}{a}, 那麼後面那個式子就變成了x+y+kx+ykx+y+kx+\frac{y}{k}

這是個啥玩意呢? 畫出平面直角座標系可以發現這玩意就是斜率爲k-k, 經過點(x,y)(x,y)的直線在xxyy軸上的截距之和。

然後發現可能成爲最大值的點一定在一個上凸包上, 搞一搞就好。

由均值不等式可得這個函數的最小值在k=yxk=\sqrt \frac{y}{x}處取到, 這個點成爲最大值的區間就是相鄰兩條線段的斜率之間的部分, 判一判是不是在這個區間內就好了。

注意右上凸包兩端的點的橫縱座標分別加上EPS, 避免出現nan的情況。

代碼如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <iostream>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define db long double
#define MX 1000050
#define EPS 1e-8
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
int n, cnum;
int conv[MX];
struct Point {db x, y;}dat[MX];
IN Point operator + (const Point &x, const Point &y) {return {x.x + y.x, x.y + y.y};}
IN Point operator - (const Point &x, const Point &y) {return {x.x - y.x, x.y - y.y};}
IN db operator * (const Point &x, const Point &y) {return x.x * y.y - x.y * y.x;}
IN bool operator < (const Point &x, const Point &y) {return x.x == y.x ? x.y > y.y : x.x < y.x;}
IN db Getk(const Point &x) {return x.y / x.x;}
IN void Getconv()
{
	std::sort(dat + 1, dat + 1 + n);
	for (R int i = 1; i <= n; ++i)
	{
		W (cnum >= 2 && (dat[conv[cnum]] - dat[conv[cnum - 1]]) * (dat[i] - dat[conv[cnum - 1]]) >= 0) --cnum;
		conv[++cnum] = i;
	}
}
int main(void)
{
	db mxx = 0, mxy = 0, ratl, ratr, best;
	scanf("%d", &n);
	for (R int i = 1; i <= n; ++i)
	{
		scanf("%Lf%Lf", &dat[i].x, &dat[i].y);
		mxx = max(mxx, dat[i].x), mxy = max(mxy, dat[i].y);
	}
	dat[++n] = {0, mxy}, dat[++n] = {mxx, 0};
	Getconv(); db ans = 1e18;
	dat[conv[cnum]].x += EPS;
	dat[conv[1]].y += EPS;
	for (R int i = 2; i < cnum; ++i)
	{
		ratl = -Getk(dat[conv[i]] - dat[conv[i - 1]]);
		ratr = -Getk(dat[conv[i + 1]] - dat[conv[i]]);
		ans = min(ans, dat[conv[i]].x + dat[conv[i]].y + ratl * dat[conv[i]].x + dat[conv[i]].y / ratl);
		ans = min(ans, dat[conv[i]].x + dat[conv[i]].y + ratr * dat[conv[i]].x + dat[conv[i]].y / ratr);
		best = std::sqrt(dat[conv[i]].y / dat[conv[i]].x);
		if (best >= ratl && best <= ratr) ans = min(ans, dat[conv[i]].x + dat[conv[i]].y + best * dat[conv[i]].x + dat[conv[i]].y / best);
	}
	printf("%.4Lf", ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章