洛谷
一家金券交易所只發行交易兩種金券: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...