NOI2007 貨幣兌換

2022-05-08 06:18:09 字數 4279 閱讀 2188

洛谷

一家金券交易所只發行交易兩種金券:a券和b券。

我們記錄第\(k\)天中a券和b券的價值分別為\(a_k\)和\(b_k\)(元/單位金券)

交易所提供兩種操作:

a)賣出金券:顧客將\(op\%\)的a券和\(op\%\)的b券以當時的價值兌換為人民幣;

b)**金券:顧客支付\(ip\)元人民幣,交易所將會兌換給使用者總價值為\(ip\)

的金券,並滿足提供給顧客的a券和b券的比例在第k天恰好為\(rate_k\)。

給出未來\(n\)天內的\(a_k\),\(b_k\)和\(r_k\),

開始時擁有\(s\)元錢,求出\(n\)天後最多能夠獲得多少元錢。

必然存在一種最優的買賣方案滿足:

每次買進操作使用完所有的人民幣;

每次賣出操作賣出所有的金券。

\[n≤10^5,0

既然題面都這麼友善那我們就照著題目來吧

先分析一下買進和賣出操作:

買進:使用\(ip\)元,**\(\frac\)張a券和\(\frac\)張b券

賣出:賣出\(f_a\)張a券和\(f_b\)張b券,獲得收入\(f_aa_k+f_bb_k\)元

在這種極端的操作下,我們發現:

購買方案一定由多個不相交的**——賣出序列構成的

那麼我們考慮一次**——賣出操作,在第\(k\)天**,第\(k'\)天賣出;

買進:使用\(s\)元,**\(\frac\)張a券和\(\frac\)張b券

賣出:賣出\(\frac\)張a券和\(\frac\)張b券,

獲得收入\((a_\frac+b_\frac)\)元

兩者之比為\(\fracr_k+b_}\)

於是我們考慮建立dp方程:

設\(f[i]\)為前\(i\)天所能獲得的最大比值,\(f[1]=1\),有

\[f[i]=max\^\frac\}

\]最後答案為\(max_^f[i]\),這時複雜度是\(o(n^2)\)的。

update 10.15:感謝@mona指出了本蒟蒻dp方程和最後答案的錯誤,之前這裡寫的是\(f[n]\)

考慮斜率優化,

\[f[i]=b_i\times max_^[\frac+1)}]

\]\[=b_i\times max_^(\frac\times\frac+\frac)

\]插點\((\frac,\frac)\),詢問\(k_i=-\frac\)即可?

然而\(x_j=\frac\)並不是單增的,因此我們要考慮如何維護這個上凸包

那麼...本篇題解的重點

由於splay可以把橫座標排序,那麼我們只需要動態處理縱座標即可

每個點最多進入splay一次,被彈出一次,兩次時間都是\(o(logn)\),

所以splay維護的總複雜度就是\(o(nlogn)\)

查詢凸包值的時候,在splay上二分即可,總複雜度是\(o(nlog^2n)\)

那麼這道題實際是一道資料結構題

調了\(2.5h\)終於成功

似乎有還有一種cdq分治的做法,但由於本人太弱因此只能用splay

update 9.19:終於學了cdq分治,補完本篇

因為\(cdq\)分治可以消除一維偏序的影響,所以我們用其保證\(x\)單調即可

因為序列\(dp\)是從前往後進行的,因此分治的步驟和傳統的\(cdq\)有些不同

其實這已經有點類似於線段樹分治了..

這裡是\(splay\)的**

#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#define mp make_pair

#define pub push_back

#define puf push_front

#define pob pop_back

#define pof pop_front

#define rg register

#define il inline

#define update(i) (sz[i]=sz[s[0][i]]+sz[s[1][i]]+1)

#define isr(i) (s[1][fa[i]]==i)

using namespace std;

typedef unsigned long long ull;

typedef vectorvi;

typedef long long ll;

typedef double dd;

const dd eps=1e-9;

const int mod=1e8;

const int n=100010;

const int inf=2147483647;

il ll read()

il void file()

ll n,s,rt,cnt;dd a[n],b[n],r[n],f[n],x[n],y[n];

int fa[n],s[2][n],sz[n];

il void print(int);

il void rot(int i)

il void splay(int i,int a)

il dd calc(int i,int j)

il void run_pop(int i)

splay(i,0);

if(nxt)splay(nxt,i);

while(s[1][nxt]) }

il void insert(dd rx,dd ry)

return;

} if(rxx[i])

} if(lst&&nxt&&calc(lst,i)>1;

ret=check(mid,k,ans);

if(ret==0)return ans;

else if(ret==1)l=mid+1;

else if(ret==-1)r=mid-1; }}

int main()

printf("%.3lf\n",s*1.0*f[n]);

return 0;

}

這裡是\(cdq\)分治的**

#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#define cpy(x,y) memcpy(x,y,sizeof(x))

#define set(x,y) memset(x,y,sizeof(x))

#define file "a"

#define mp make_pair

#define pb push_back

#define rg register

#define il inline

using namespace std;

typedef unsigned long long ull;

typedef vectorvi;

typedef long long ll;

typedef double dd;

const dd eps=1e-7;

const int mod=1e9+7;

const int n=100010;

const int m=1000010;

const int inf=2147483647;

const ll inf=1e18+1;

const ll p=100000;

il ll read()

il void file()

int n,q[n],tmp[n];

dd s,a[n],b[n],r[n],f[n],x[n],y[n],k[n];

dd cx[n],cy[n];int top;

il dd getk(dd ax,dd ay,dd bx,dd by)

cdq(m+1,r);

for(rg int i=m+1;i<=r;i++)

for(rg int i=l,p1=l,p2=m+1;i<=r;i++)

if(p2>r||(p1<=m&&x[q[p1]]else tmp[i]=q[p2++];

for(rg int i=l;i<=r;i++)q[i]=tmp[i];

}int main()

NOI2007 貨幣兌換

今天聽了crazy和samjia的noi雜 砸 題選講,感覺自己萌萌噠 於是就來怡情地寫了這道題。額 o 這個不好說啊。語文不好不好裱我 還是貼圖吧。咳咳,希望大家都看懂題了。乙個很明顯的貪心思路就是,我們每天要不全買,要不全賣。因為一有利益我們就去佔,一有虧損我們就不碰。那麼我們可以有dp方程 f...

Noi2007 貨幣兌換

傳送門 小半個上午 一下午都給了這題了qaq 都知道是斜率優化,問題是我看這題根本就是乙個人乙個式子啊 算了,把我的過程列出來吧 令 f i 表示第i天結束時強制賣出所有金券最多能得到的軟妹幣數量,列舉上一次 金券的時間,就有 beginf i max end 這裡沒有寫隱含條件 f i ge f ...

NOI2007 貨幣兌換

題目 先來畫一畫柿子 設 dp i 表示你第 i 天之後最多剩下多少錢 考慮一下對於 i 的轉移,我們肯定要在之前列舉一天 j 這一天把所有的東西買進來,之後在 i 天賣掉 設那天買進 a 的量為 d a 買進 b 的量為 d b 我們可以得到這樣的方程 d ap a d bp b dp j d a...