1. 程式人生 > >[bzoj1492][cdq分治][斜率優化][NOI2007]貨幣兌換Cash

[bzoj1492][cdq分治][斜率優化][NOI2007]貨幣兌換Cash

1492: [NOI2007]貨幣兌換Cash

Time Limit: 5 Sec Memory Limit: 64 MB
Submit: 5063 Solved: 2075
[Submit][Status][Discuss]
Description

小Y最近在一家金券交易所工作。該金券交易所只發行交易兩種金券:A紀念券(以下簡稱A券)和 B紀念券(以下
簡稱B券)。每個持有金券的顧客都有一個自己的帳戶。金券的數目可以是一個實數。每天隨著市場的起伏波動,
兩種金券都有自己當時的價值,即每一單位金券當天可以兌換的人民幣數目。我們記錄第 K 天中 A券 和 B券 的
價值分別為 AK 和 BK(元/單位金券)。為了方便顧客,金券交易所提供了一種非常方便的交易方式:比例交易法
。比例交易法分為兩個方面:(a)賣出金券:顧客提供一個 [0,100] 內的實數 OP 作為賣出比例,其意義為:將
OP% 的 A券和 OP% 的 B券 以當時的價值兌換為人民幣;(b)買入金券:顧客支付 IP 元人民幣,交易所將會兌
換給使用者總價值為 IP 的金券,並且,滿足提供給顧客的A券和B券的比例在第 K 天恰好為 RateK;例如,假定接
下來 3 天內的 Ak、Bk、RateK 的變化分別為:

假定在第一天時,使用者手中有 100元 人民幣但是沒有任何金券。使用者可以執行以下的操作:

注意到,同一天內可以進行多次操作。小Y是一個很有經濟頭腦的員工,通過較長時間的運作和行情測算,他已經
知道了未來N天內的A券和B券的價值以及Rate。他還希望能夠計算出來,如果開始時擁有S元錢,那麼N天后最多能
夠獲得多少元錢。
Input

輸入第一行兩個正整數N、S,分別表示小Y能預知的天數以及初始時擁有的錢數。接下來N行,第K行三個實數AK、B
K、RateK,意義如題目中所述。對於100%的測試資料,滿足:0 < AK ≤ 10;0 < BK ≤ 10;0 < RateK ≤ 100;MaxProfit ≤ 1
0^9。
【提示】
1.輸入檔案可能很大,請採用快速的讀入方式。
2.必然存在一種最優的買賣方案滿足:
每次買進操作使用完所有的人民幣;
每次賣出操作賣出所有的金券。
Output

只有一個實數MaxProfit,表示第N天的操作結束時能夠獲得的最大的金錢數目。答案保留3位小數。

Sample Input

3 100

1 1 1

1 2 2

2 2 3
Sample Output

225.000

對於這題我只有mmp可說
sol:
f[i]表示在i這天把所有貨幣都賣掉的最優價值
f[i]=max{f[j]/(a[j]*rate[j]+b[j])*rate[j]*a[i]+f[j]/(a[j]*rate[j]+b[j])*b[i]}
設x[j]=f[j]/(a[j]*rate[j]+b[j])*rate[j]表示第j天最多可以擁有的A貨幣的數量
y[j]=f[j]/(a[j]*rate[j]+b[j])表示第j天最多可以擁有的B貨幣的數量

那麼f[i]=max(x[j]*a[i]+y[j]*b[i])
很interesting,這很像斜率優化(個屁,還沒見過2個i的斜率優化

嘗試性優化了一下
-a[i] * x[j]+(x[j] * a[i]+y[j]b[i])=y[j] b[i]

-a[i]/b[i] * x[j]+(x[j]*a[i]/b[i]+y[j])=y[j]
得到了點(x[j],y[j]),要使截距最大,看起來很斜率了(個屁,為啥一點都不單調
不單調怎麼做?
我們讓他變成單調的,用cdq分治,設solve(l,r)表示,區間l,r中的f[i]值全部得到的一個操作。
對於一個solve操作
首先我們將左區間solve,這部分只能用來更新右區間(內部更新完了),然後再solve右區間。
考慮左邊怎麼更新右邊
這時候我們就能排序左區間把整個凸殼弄出來啦
右區間也能排序使點單調辣
然後就沒了,單調棧掃一掃就行了
(注意精度,我卡了2hour才注意到)
還有為什麼cmp函式會有問題,會的選手帶帶我啊,我不怎麼會搞精度之類的活

#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
int n;
double m;
const int N=120000;
const double eps=1e-9;
int sta[N],top;
struct cc
{
    int id;
    double a,b,x,y,f,rate;
}a[N];
inline bool check(int x,int y,int z)//遞增為1
{
    double A=a[y].y-a[x].y,B=a[z].y-a[y].y,C=a[y].x-a[x].x,D=a[z].x-a[y].x;
    return A*D<B*C+eps;
}
inline bool cmp1(const cc &a,const cc &b)
{
    return a.x+eps<b.x+eps||fabs(a.x-b.x)<=eps&&a.y+eps<b.y+eps;
}
inline bool cmp2(const cc &a,const cc &b)
{
    return a.a*b.b+eps<b.a*a.b+eps;
}
inline bool cmpid(const cc &a,const cc &b)
{
    return a.id<b.id;
}
void solve(int l,int r)
{
    if(l==r)
    {
        a[l].f=max(a[l].f,a[l-1].f);
        a[l].y=a[l].f/(a[l].a*a[l].rate+a[l].b);
        a[l].x=a[l].y*a[l].rate;
        return;
    }
    int mid=l+r>>1,x,y,top=0;
    solve(l,mid);
    sort(a+l,a+1+mid,cmp1);
    for(int j=l;j<=mid;++j)
    {
        while(top>1&&check(sta[top-1],sta[top],j)) --top;
        sta[++top]=j;
    }
    sort(a+mid+1,a+r+1,cmp2);
    int t=1;
    for(int i=mid+1;i<=r;++i)
    {
        while(-a[i].a/a[i].b*(a[sta[t+1]].x-a[sta[t]].x)<(a[sta[t+1]].y-a[sta[t]].y)&&t<top) t++;
        a[i].f=max(a[i].f,(a[sta[t]].x*a[i].a/a[i].b+a[sta[t]].y)*a[i].b);
    }
    sort(a+l,a+r+1,cmpid);
    solve(mid+1,r);
}
int main()
{
//  freopen("cash.in","r",stdin);
//  freopen("cash.out","w",stdout);
    scanf("%d%lf",&n,&m);
    for(int i=1;i<=n;++i)
    {
        scanf("%lf%lf%lf",&a[i].a,&a[i].b,&a[i].rate);
        a[i].id=i;
        a[i].f=m;
    } 
    solve(1,n);
    sort(a+1,a+1+n,cmpid);
    printf("%.3lf",a[n].f);
}