1. 程式人生 > >LUOGU P4027 [NOI2007]貨幣兌換 (斜率優化+CDQ分治)

LUOGU P4027 [NOI2007]貨幣兌換 (斜率優化+CDQ分治)

ace sca pre noi 正是 while opened 一個點 const

傳送門

解題思路

題目裏有兩句提示一定要看清楚,要不全買要不全賣,所以dp方程就比較好列,f[i]=max(f[j]*rate[j]*a[i])/(rate[j]*a[j]+b[j])+(f[j]*b[i])/(rate[j]*a[j]+b[j]),意義就是在從前面的某一天買入,這一天賣出,時間復雜度O(n^2),這樣只有60分,,考慮優化。設在j這天a買入了x[j]股,則x[j]=(rate[j]*f[j])/(rate[j]*a[j]+b[j]),b買入了y[j]股,則y[j]=rate[j]/(rata[j]*a[j]+b[j]),那麽轉移方程就可以寫成f[i]=x[j]*a[i]+y[j]*b[i],那麽變形之後y[j]=x[j]*(a[i]/b[i])+f[i]/b[i],這不正是y=kx+b的形式,現在要求的就是用一個a[i]/b[i]斜率的直線去過x[j],y[j]這些點,使得截距最大,這正是斜率優化。但是發現這個東西只有f具有單調性,不能用單調數據結構維護,看了大佬們的博客發現可以用cdq維護。首先維護的一定是一個斜率遞減的凸包,因為斜率一定為負。其次對於一條a[i]/b[i]來說,如果當前點與上一個點的斜率更小,那麽向右移動可以使得截距更大,這樣就可以用cdq來維護,首先按照k排序,然後cdq分治裏x這一維,就可以很玄學的轉移了。

技術分享圖片
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>

using namespace std;
const int MAXN = 100005;
const double inf = 1e9;
const double eps = 1e-6;

int n,stk[MAXN];
double f[MAXN];

struct Query{
    int id;
    
double x,y,k,a,b,rate; }q[MAXN],tmp[MAXN]; inline bool cmp(Query A,Query B){ return A.k<B.k; } inline double slope(int A,int B){ if(q[A].x==q[B].x) return inf; return (q[A].y-q[B].y)/(q[A].x-q[B].x); } void cdq(int l,int r){ if(l==r){ f[l]=max(f[l],f[l-1]); q[l].y
=f[l]/(q[l].rate*q[l].a+q[l].b); q[l].x=q[l].y*q[l].rate; return; } int mid=l+r>>1;int t1=l-1,t2=mid,top=0; for(register int i=l;i<=r;i++) { if(q[i].id<=mid) tmp[++t1]=q[i]; else tmp[++t2]=q[i]; } for(register int i=l;i<=r;i++) q[i]=tmp[i]; cdq(l,mid); for(register int i=l;i<=mid;i++){ while(top>=2 && slope(stk[top-1],stk[top])<=slope(stk[top],i)+eps) top--; stk[++top]=i; } for(register int i=mid+1;i<=r;i++){ while(top>=2 && slope(stk[top-1],stk[top])<=q[i].k+eps) top--; int j=stk[top]; f[q[i].id]=max(f[q[i].id],q[j].x*q[i].a+q[j].y*q[i].b); } cdq(mid+1,r); int L=l,R=mid+1,o=0; while(L<=mid && R<=r){ if(q[L].x<q[R].x+eps) tmp[++o]=q[L++]; else tmp[++o]=q[R++]; } while(L<=mid) tmp[++o]=q[L++]; while(R<=r) tmp[++o]=q[R++]; for(register int i=l;i<=r;i++) q[i]=tmp[i-l+1]; } int main(){ scanf("%d%lf",&n,&f[0]); for(int i=1;i<=n;i++) { scanf("%lf%lf%lf",&q[i].a,&q[i].b,&q[i].rate); q[i].k=-q[i].a/q[i].b;q[i].id=i; } sort(q+1,q+1+n,cmp);cdq(1,n); printf("%.3lf",f[n]); return 0; }
View Code

LUOGU P4027 [NOI2007]貨幣兌換 (斜率優化+CDQ分治)