BZOJ1492【NOI2007】货币兑换

来源:互联网 发布:三k党 知乎 编辑:程序博客网 时间:2024/04/29 16:59

题目链接: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;}


0 0
原创粉丝点击