設\(dp[i],a[i],b[i],x[i],y[i]\)為第\(i\)天時的最大收益/a卷**/b卷**/將所有現金兌成a卷數量/b卷
於是有\[dp[i]=\max(dp[i-1],x[j]*a[i]+y[j]*b[i])
\]然後可以寫成斜率優化形式
\[y[j]=-\fracx[j]+\frac
\]對於每個點\((x[j],y[j])\),我們用一根斜率為\(-\frac\)的直線去經過它,使截距最大,也就是維護上凸包
因為每一點x座標,斜率都不單調,所以上平衡樹 cdq
先把所有點按k遞增順序排好,再按天數分治,使得處理時是按天數遞增的順序
因為這是dp,所以要先處理左區間,再遞迴右區間
在處理影響時我們不用管左區間的順序,也就是可以按x座標遞增插入凸包裡
遞迴出口時首先\(dp[i]=\max(dp[i],dp[i-1])\),然後再計算\(y[i]=\frac,x[i]=y[i]*a[i]\)
加上歸併把複雜度降低到\(o(n\log n)\)
注意這道題似乎有點卡精度(雖然我沒有)
#includeusing namespace std;
#define maxn 100005
#define db double
#define eps 1e-9
#define inf 1e18
db dp[maxn];
int n, sta[maxn];
struct cash
friend void read(cash& p)
}cash[maxn], tp[maxn];
db getk(int a, int b)
void merge_sort(int l, int r, int mid)
while (t1 <= mid) tp[k++] = cash[t1++];
while (t2 <= r) tp[k++] = cash[t2++];
for (int i = l; i <= r; ++i) cash[i] = tp[i];
}void cdq(int l, int r)
int mid = (l + r) >> 1, top = 0;
for (int i = l, t1 = l, t2 = mid + 1; i <= r; ++i)//先按天數排序,遞迴
for (int i = l; i <= r; ++i) cash[i] = tp[i];
cdq(l, mid);
for (int i = l; i <= mid; ++i)//維護左區間的上凸包
for (int i = mid + 1; i <= r; ++i)//更新右區間答案
cdq(mid + 1, r);
merge_sort(l, r, mid);
}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...