题解 NOIP 2018
令 \(f(x)\) 为兑换 \(x\) 个A种货币时的最大兑换数
于是 \(f(x)\) 是个单峰函数,可以三分
但会炸精度,于是需要代回check一下是否合法以及是否有更优解
然后还有函数图像特别平的时候精度不太够,需要单独check一下两个端点
复杂度 \(O(nlogn)\)
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
#define int long long
#define int128 __int128
#define ld long double
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int q, a, b, c, d, x;
namespace force{
double uplim(double a, double b, double c) {return (-b+sqrt(b*b-4.0*a*c))/(2.0*a);}
int cost1(int k) {return (a-b)*k+b*k*(k+1)/2;}
void solve() {
int lim=uplim(b/2.0, (a-b/2.0), -x), ans=0;
// cout<<"lim: "<<lim<<endl;
for (int i=0; i<=lim; ++i) {
double tem=uplim(d/2.0, (c-d/2.0), -x+cost1(i));
ans=max(ans, i+(int)(tem));
// cout<<i+tem<<' ';
} //cout<<endl;
printf("%lld\n", ans);
}
}
namespace task1{
ld uplim(ld a, ld b, ld c) {return (-b+sqrt(b*b-4.0*a*c))/(2.0*a);}
ld cost1(ld k) {return (a-b)*k+b*k*(k+1)/2;}
ld calc(ld k) {
ld tem=uplim(d/2.0, (c-d/2.0), -x+cost1(k));
return k+tem;
}
void solve() {
int lside=0, rside=uplim(b/2.0, (a-b/2.0), -x), lmid, rmid; ld ans=0;
while (lside<=rside) {
lmid=(lside+rside)>>1; rmid=lmid+1;
if (lmid<=lside || lmid>=rside || rmid >=rside) break;
if (calc(lmid)>=calc(rmid)) rside=rmid;
else lside=lmid;
}
// cout<<"side: "<<lside<<' '<<rside<<endl;
for (int i=lside; i<=rside; ++i) ans=max(ans, calc(i));
printf("%lld\n", (ll)(ans));
}
}
namespace task{
int128 tran=1;
ld uplim(ld a, ld b, ld c) {return (-b+sqrt(b*b-4.0*a*c))/(2.0*a);}
ld cost1(ld k) {return (a-b)*k+b*k*(k+1)/2;}
ld calc(ld k) {
ld tem=uplim(d/2.0, (c-d/2.0), -x+cost1(k));
return k+tem;
}
int128 qcst1(int128 k) {return tran*(a-b)*k+tran*k*(k+1)/2*b;}
int128 qcst2(int128 k) {return tran*(c-d)*k+tran*k*(k+1)/2*d;}
ll confirm(ll k) {
int128 tem=uplim(d/2.0, (c-d/2.0), -x+cost1(k));
while (tem>=0 && qcst2(tem)>x-qcst1(k)) --tem;
while (qcst2(tem+1)<=x-qcst1(k)) ++tem;
return k+tem;
}
ll same() {
int l=0, r=1e9, mid;
while (l<=r) {
mid=(l+r)>>1;
if (qcst1(mid/2)+qcst2(mid-mid/2)<=x) l=mid+1;
else r=mid-1;
}
return l-1;
}
void solve() {
if (a==c && b==d) {printf("%lld\n", same()); return ;}
int lside=0, rside=uplim(b/2.0, (a-b/2.0), -x), lmid, rmid; ll ans=0;
// cout<<"lim: "<<rside<<endl;
ans=max(ans, confirm(lside));
ans=max(ans, confirm(rside));
while (lside<=rside) {
lmid=(lside+rside)>>1; rmid=lmid+1;
if (lmid<=lside || lmid>=rside || rmid >=rside) break;
ans=max(ans, confirm(lmid));
ans=max(ans, confirm(rmid));
if (calc(lmid)>=calc(rmid)) rside=rmid;
else lside=lmid;
}
// for (int i=1; i<=7; ++i) printf("%d:%.14Lf ", i, calc(i)); cout<<endl;
// for (int i=1; i<=7; ++i) printf("%d:%lld ", i, confirm(i)); cout<<endl;
// cout<<"side: "<<lside<<' '<<rside<<endl;
if (qcst1(rside+1)<=x) ++rside;
for (int i=lside; i<=rside; ++i) ans=max(ans, confirm(i)); //, printf("%d:%lld\n", i, confirm(i));
printf("%lld\n", ans);
}
}
signed main()
{
freopen("money.in", "r", stdin);
freopen("money.out", "w", stdout);
q=read();
for (int i=1; i<=q; ++i) {
a=read(); b=read(); c=read(); d=read(); x=read();
// force::solve();
task::solve();
}
return 0;
}