1. 程式人生 > >[BZOJ1492]]NOI2007]cash-[cdq分治]

[BZOJ1492]]NOI2007]cash-[cdq分治]

while 一個 所有 信息 php col online 凸包 並不是

Description

傳送門

Solution

首先,最優情況一定是某一天把所有金券賣出或買入是最優的。

在金券一定的情況下,分散賣一定沒有統一在最優的那天賣更優。

然後,我們假定在某一天賣,則在該天前面一定會有一天的全部買入能夠使價值最大。

定義ans[i]為第i天能擁有的最大錢數。

則第i天能夠有的A金券數為x[i]=ans[i]/(a[i]*rate[i]+b[i])*rate[i],y[i]=ans[i]/(a[i]*rate[i]+b[i])。

ans[i]=max{x[j]*a[i]+y[j]*b[i],ans[i-1]}。(ans[i-1]是假如說該天最優是某一天往後的不買也不賣的情況,因為有時候並不是一定要買賣才是最優的。)

y[j]*b[i]=ans[i]-x[j]*a[i]

y[j]=x[j]*(-a[i]/b[i])+ans[i]/b[i]。

則我們要ans[i]最大,實際上就是在j<i的每一對(x[j],y[j])上畫k=-a[i]/b[i]的直線,找最大的截距。這個一看就是凸包的套路。

但是關鍵是,(x[j],y[j])和直線k的斜率都是沒有規律的(即沒有遞增或遞減的情況),直接處理很麻煩。

我們考慮cdq分治。(一天為一個操作,一個操作裏包含了該天的信息)

初始情況下,將所有的操作按斜率k來排序。當某一個子問題處理完畢後,將子問題內的所有操作按x坐標排序。這樣,當我們分治處理時,[l,mid]內的所有操作都按x排序,[mid+1,r]內的所有操作都按斜率k排序了,當然也必須保證[l,mid]的操作順序(即第幾天)在[mid+1,r]之前。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const double eps=1e-9;
int n;

double ans[100010];
struct node{double a,b,k,rate,x,y;int id;
}w[100010],q[100010];
int st[100010];


double K(int x,int y)
{if (!y) return
-1e20; if (fabs(w[x].x-w[y].x)<eps) return 1e20; return (w[y].y-w[x].y)/(w[y].x-w[x].x);} bool cmp1(node a,node b){return a.k<b.k;} bool cmp2(node a,node b) {return a.x<b.x;}//? void cdq(int l,int r) { if (l==r) { ans[l]=max(ans[l],ans[l-1]); w[l].y=ans[l]/(w[l].a*w[l].rate+w[l].b); w[l].x=w[l].y*w[l].rate; return; } int mid=(l+r)/2,js0=l,js1=mid+1,tp=0; for (int i=l;i<=r;i++) if (w[i].id<=mid) q[js0++]=w[i];else q[js1++]=w[i]; for (int i=l;i<=r;i++) w[i]=q[i]; cdq(l,mid); for (int i=l;i<=mid;i++) { while (tp>1&&K(st[tp],st[tp-1])<K(i,st[tp-1])+eps) tp--; st[++tp]=i; } int j=1; for (int i=mid+1;i<=r;i++) { while (tp>1&&w[i].k>K(st[tp],st[tp-1])-eps) tp--; ans[w[i].id]=max(ans[w[i].id],w[st[tp]].x*w[i].a+w[st[tp]].y*w[i].b); } cdq(mid+1,r);js0=l;js1=mid+1; merge(w+l,w+mid+1,w+mid+1,w+r+1,q+l,cmp2); for (int i=l;i<=r;i++) w[i]=q[i]; } int main() { scanf("%d%lf",&n,&ans[0]); for (int i=1;i<=n;i++) { scanf("%lf%lf%lf",&w[i].a,&w[i].b,&w[i].rate);w[i].id=i; w[i].k=-1.0*w[i].a/w[i].b; } sort(w+1,w+n+1,cmp1); cdq(1,n); printf("%.3f",ans[n]); }

[BZOJ1492]]NOI2007]cash-[cdq分治]