1. 程式人生 > >BZOJ 1492 貨幣兌換 cdq分治或平衡樹維護凸包

BZOJ 1492 貨幣兌換 cdq分治或平衡樹維護凸包

hid osi this hidden left javascrip 一個 display block

題意:鏈接

方法:cdq分治或平衡樹維護凸包

解析:

這道題我拒絕寫平衡樹的題解,我僅僅想說splay不要寫掛,insert邊界條件不要忘。del點的時候不要腦抽d錯。有想寫平衡樹的去看140142或者留言我。

首先這道題能推出個表達式

f[i]代表第i天最大收益。

xx[i]表示將第i天的錢都買A的數量

yy[i]表示將第i天的錢都買B的數量

所以f[i]=max(f[i?1],p[i].a?xx[j]+p[i].b?yy[j])j<i

所以我們要維護這個n^2的遞推式

又知道f[i]是由小於i的j更新的,

但方程要進一步寫一下

yy[i]=(-p[i].a/p[i].b)*xx[i]+f[i]/p[i].b

所以我們要得到最大截距所以能夠依照斜率遞減維護一個凸包來找某一確定直線與這個凸包截得的最大截距,也就是斜率第一個小於等於它的某個凸包上的點。

之後的部分就是採用cdq維護或者平衡樹

平衡樹真是一個噩夢

代碼:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100010 #define eps 1e-9 #define INF 0x7fffffff using namespace std; typedef long long ll; int n; double f[N]; int stack[N]; struct node { double x,y,a,b,rate,k; int w; }p[N],t[N]; int cmp(node a,node b) { return a.k>b.k; } double getk(int a,int b) { if(!b)return -INF; if
(fabs(p[a].x-p[b].x)<eps)return INF; return (p[b].y-p[a].y)/(p[b].x-p[a].x); } void solve(int l,int r) { if(l==r) { f[l]=max(f[l-1],f[l]); p[l].y=f[l]/(p[l].a*p[l].rate+p[l].b); p[l].x=p[l].rate*p[l].y; return; } int mid=(l+r)>>1; int l1=l,l2=mid+1,pt=1; for(int i=l;i<=r;i++) { if(p[i].w<=mid)t[l1++]=p[i]; else t[l2++]=p[i]; } for(int i=l;i<=r;i++)p[i]=t[i]; solve(l,mid); int top=0; for(int i=l;i<=mid;i++) { while(top>1&&getk(stack[top-1],stack[top])<=getk(stack[top],i))top--; stack[++top]=i; } stack[++top]=0; for(int i=mid+1;i<=r;i++) { while(pt<top&&getk(stack[pt],stack[pt+1])>p[i].k)pt++; f[p[i].w]=max(f[p[i].w],p[stack[pt]].x*p[i].a+p[stack[pt]].y*p[i].b); } solve(mid+1,r); l1=l,l2=mid+1; for(int i=l;i<=r;i++) if(((p[l1].x<p[l2].x||(fabs(p[l1].x-p[l2].x)<eps&&p[l1].y<p[l2].y))||l2>r)&&l1<=mid)t[i]=p[l1++]; else t[i]=p[l2++]; for(int i=l;i<=r;i++)p[i]=t[i]; } int main() { 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].w=i; } sort(p+1,p+1+n,cmp); solve(1,n); printf("%.3lf\n",f[n]); }

BZOJ 1492 貨幣兌換 cdq分治或平衡樹維護凸包