题解
这道题教会我很多东西,虽然它是个傻逼三分
1.long double的运算常数是巨大的
2.三分之前的界要算对!一定要算准,不要想一个直接写上!
3.三分100次也就只能把精度往里推20多位,可你需要的精度是最大数值加小数位,大概是12位,而你三分的界最小也得卡到1e4和1e-4(虽然,用1e3和1e-3作为界也能过,我可不想考场上赌这个东西= =),精度爆炸……如何优化……
(其实atk和dnf小一点能更不卡精度吧= =)
(这样的题根本连调都调不出来,就算我界算对了也得被三分次数卡死= =)
好的我们来说这道题
首先思路很明显,我们可以推一只妖怪的攻击力的表达式
(frac{a}{b}dnf + frac{b}{a}atk + atk + dnf)
我们设(k = frac{b}{a})
那么(atk cdot k + dnf frac{1}{k} + atk + dnf)我们发现这是一个对勾函数
啥,你说你不知道什么是对勾函数数学课是不是又逃课了
(自行描点画一下(k + frac{1}{k}))
这个东西在第一象限的图像是个单峰函数,这样n个单峰函数复合在一起求最大值还是一个单峰函数,可以三分
复杂度(O(n log_3 n))
鉴于我死的很惨,我讲讲怎么算界
首先这个东西要取到最小值的话大概是
(atk cdot k = dnf frac{1}{k})可以得出k大概是根号下dnf和atk的比值,大概是1e-4到1e4
然而你即使三分90次还是会跪
仗着LOJ很快我三分了100次……过掉了
其实1e3也能AC但是= =这会被非常极限的数据卡死,完全就是赌出题人到底善不善良
代码
#include <bits/stdc++.h>
//#define ivorysi
#define MAXN 1000005
typedef long long int64;
typedef unsigned int u32;
using namespace std;
const int MOD = 1000000007;
int n;
double atk[MAXN],dnf[MAXN];
double calc(double k) {
double res = 0.0;
for(int i = 1 ; i <= n ; ++i) {
res = max(res,atk[i] + dnf[i] + k * atk[i] + (1.0 / k) * dnf[i]);
}
return res;
}
void Solve() {
scanf("%d",&n);
for(int i = 1 ; i <= n ; ++i) {
scanf("%lf%lf",&atk[i],&dnf[i]);
}
int cnt = 100;
double l = 1e-4,r = 1e4;
while(cnt--) {
double k = (r - l) / 3.0;
double lb = l + k,rb = r - k;
if(calc(lb) > calc(rb)) l = lb;
else r = rb;
}
printf("%.4lf",calc(r));
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
}