Description
已知一种商品的购买人数n和价格p之间的关系是
n=⌊n0−pk⌋
,收益是
n0(p−p0)
。
或者有两种商品,购买第一种商品的人数n1和价格p1满足
n1=⌊n0−p1k⌋
,第二种商品的人数n2和价格p2满足
n2=⌊n0−p2k⌋−n1
,其总收益为
n1(p1−p0)+n2(p2−p0)
。
给出n0,p0,k,求两种情况下的最大收益。
n0<=10^6
Solution
感觉中学生数学题比小学生简单好多呀~
那么下一次是不是幼儿园数学题?
咳咳
首先我们发现,这个下取整可以扔掉了,因为对于每一个可以达到某一个n的p,肯定是最大的那一个要保留。
然后,第一问的收益就是关于人数的二次函数。
果断三分求极值。
后来被出题人表♂了一下才知道有O(1)的算法。
就是取中点。
所以infleaking同学(学习栋爷)才能用二分水过~
第二问可以循环加三分。
但是对于极限数据会超时。
于是有梦♂想的我坚信第一维也是可以三分的。。。
于是拍了2h+。。。
结果O(1)是三等分点。。。
O(≧口≦)O
Code
#include<cstdio>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef double db;
typedef long long ll;
int n0,n;
db p0,k,ans;
db solve(ll x) {
db p=(n-x)*1.0/k;
return x*(p-p0);
}
db calc(int x) {
db p=(n0-x)*1.0/k;n=n0-x;
int l=1,r=n;
while (l<r) {
int len=(r-l)/3;
int s1=l+len,s2=s1+len+1;
if (solve(s1)<solve(s2)) l=s1+1;
else r=s2-1;
}
return x*(p-p0)+solve(l);
}
int main() {
freopen("math.in","r",stdin);
freopen("math.out","w",stdout);
scanf("%d%lf%lf",&n0,&p0,&k);
fo(i,1,n0-1) {
db p=(n0-i)*1.0/k;
ans=max(ans,i*(p-p0));
}
printf("%.4lf ",ans);
int l=1,r=n0;
while (l<r) {
int len=(r-l)/3;
int s1=l+len,s2=s1+len+1;
if (calc(s1)<calc(s2)) l=s1+1;
else r=s2-1;
}
printf("%.4lf",calc(l));
}