题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1492
【分析】
首先要利用一个贪心的思想,就是在同一天要么把钱全部换成金劵,要么把金劵全部换成钱。
于是设f[i]为第i天能得到的最多的钱,x[i]与y[i]分别表示用第i天换来的钱全部换取A、B劵的数量,那么有f[i]=max{a[i]*x[j]+b[i]*y[j]}(j<i)。
这是一个典型的可以斜率优化的式子。对于当前状态i,有决策j和k,若a[i]*x[j]+b[i]*y[j]>a[i]*x[k]+b[i]*y[k]则决策j优于k。
不妨设x[j]<x[k],上式可转化为(y[j]-y[k])/(x[j]-x[k])<-a[i]/b[i]。
然而我们发现,x是不单调的,-a[i]/b[i]也是不单调的。
平衡树维护!超长代码......
我们可以用CDQ分治!(CDQ大法好)
因为状态i只由小于i的决策转移得到,且决策转移只与其x和y的值有关,与它的位置无关,所以决策1..i-1顺序任意。不妨将决策1..i-1按x与y为关键字排序。
此时如果状态i..n的-a[i]/b[i]值是单调的,那么就可以对1..i-1维护一个凸包,用凸包上的决策更新状态i..n,时间O(n)。
于是可以每次把序列二分处理。在最开始将每一天的操作按-a[i]/b[i]排序,然后Solve(1,n)。
利用归并排序将天数(id)1..mid的操作全部放在左边,mid+1..n的操作放右边。在Solve的最后将区间内的决策按x,y排序,这样就可以O(n)维护凸包。然后利用第1..mid天的决策更新天数mid+1..n的最优值。
【代码】
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=100010;
const double eps=1e-8;
struct Node
{
int id;
double a,b,x,y,k,rate;
}P[maxn],T[maxn],*Q[maxn];
double f[maxn];
int n;
bool cmpk(const Node &a,const Node &b) {return a.k>b.k;}
bool cmpx(const Node &a,const Node &b) {return a.x<b.x || (fabs(a.x-b.x)<eps && a.y<b.y);}
double K(Node *a,Node *b) {if (fabs(a->x-b->x)<eps) return 1e50;return (a->y-b->y)/(a->x-b->x);}
void Update(int l,int r)
{
if (l==r)
{
f[l]=max(f[l],f[l-1]);
P[l].y=f[l]/(P[l].a*P[l].rate+P[l].b);
P[l].x=P[l].y*P[l].rate;
return;
}
int top=0,head=0,mid=(l+r)>>1;
Q[0]=&P[0];
for (int i=l;i<=mid;i++)
{
while (top && K(&P[i],Q[top])>K(Q[top],Q[top-1])) top--;
Q[++top]=&P[i];
}
for (int i=mid+1;i<=r;i++)
{
int x=P[i].id;
while (head<top && K(Q[head+1],Q[head])>P[i].k) head++;
f[x]=max(f[x],Q[head]->x*P[i].a+Q[head]->y*P[i].b);
}
}
void Solve(int l,int r)
{
if (l==r) {Update(l,r);return;}
int p1,p2,mid=(l+r)>>1;
p1=l;p2=mid+1;
for (int i=l;i<=r;i++) (P[i].id<=mid)?T[p1++]=P[i]:T[p2++]=P[i];
for (int i=l;i<=r;i++) P[i]=T[i];
Solve(l,mid);
Update(l,r);
Solve(mid+1,r);
p1=l;p2=mid+1;
for (int i=l;i<=r;i++) (p2>r || (cmpx(P[p1],P[p2]) && p1<=mid))?T[i]=P[p1++]:T[i]=P[p2++];
for (int i=l;i<=r;i++) P[i]=T[i];
}
void Init()
{
scanf("%d%lf",&n,&f[0]);
for (int i=1;i<=n;i++) scanf("%lf%lf%lf",&P[i].a,&P[i].b,&P[i].rate),P[i].k=-P[i].a/P[i].b,P[i].id=i;
sort(P+1,P+1+n,cmpk);
}
int main()
{
Init();
Solve(1,n);
printf("%.3f\n",f[n]);
return 0;
}