题解 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;
}
posted @ 2021-11-08 20:51  Administrator-09  阅读(0)  评论(0编辑  收藏  举报