做到一道題,不會做。
題解說是fft+ cdq分治。
學會了fft,發現不會cdq分治。
去看cdq的**,發現用了cash這道題……
然後貌似有斜率dp的東西,就順便學了斜率dp,寫了之前hdu的一道題,繼續寫這道題……
各種題解各種splay,我一想,這道題用map就行了啊……然後寫了200多行,還超級慢……
最囧的問題,在我和朋友的電腦上,用noi的資料ac,在vijos上,wa6,後面t了。
後來發現因為我偷懶,反覆呼叫map超級慢,但是不想改了。。就這樣吧,算是理解斜率dp的各種囧東西了。 以後再有空補splay寫法吧(自我安慰,不會補了……)
至於題解,網上到處都有……我再重複一次吧,如果有人需要的話。
f[i]表示第i天,所能獲得的最大 金錢數量。
如果知道了f[i],那麼我們可以用x[i]表示第i天的錢,全部買a卷,能購買的數量,y[i]則為b卷數量。
至於x[i], y[i]的求解,就不再贅述……(列方程表示一下就能求出來啦~)
然後f[i]可以寫成類似於 f[i] = max}的形式
不考慮獅子中f[i-1] 的部分,強行變換一下
f[i]=x[j]*a[i]+y[j]*b[i]
式子變形後為
f[i]/b[i] - x[j]*(a[i]/b[i])=y[j]
a[i],b[i]為常數,也就是第i天a,b卷的**。
這個式子形如y=kx+b
求i的時候,[1,i-1]的f值都已經知道了,所以可以弄出很多x[j],y[j]的點對。 相當於我在一張圖上,有很多(x,y)的座標,我有乙個斜率k,要找乙個點,去放上這個斜率k,使得方程中的b最大。
y=kx+b是截距式,並且k恒為負數(a,b為正數,式子中有符號。 同時,題目給定的rate比例是[0,100],保證了b為非0,所以斜率永遠存在~)
現在問題就轉變為,根據前面的亂七八糟的x,y的點座標,找乙個最優的點座標,來匹配上k,求出f[i]的值啦
然後顯然(這個顯然可以自己畫圖……當然別人的文章有圖啦~我比較懶) 解一定在上凸殼上,所以首先我們要維護乙個上凸殼。
對於每次插入新的點,可以看這個點在凸殼內部(下方),還是上方。因為凸殼的點 x座標是有序的,所以可以用map維護x座標,和x座標裡儲存的y座標資訊,以及這個點和左邊的點的斜率,和右邊點的斜率。
比如說,第三個點,和第二個點斜率, 以及和第四個點的斜率。維護這些資訊會方便一些~
【第乙個點和左邊的點斜率為0, 最後乙個點和右邊的點斜率為負無窮】 這個很重要,問我為啥知道的。。我舉例子弄的……沒法證明。
然後再用乙個map來維護斜率,對映斜率對應的x座標即可。
然後維護這兩個map,我的**太臃腫,並且因為偷懶,反覆呼叫了很多東西。而且確實,我的map水平不堪入目啊……
然後細節很多。。。
情況1: 插入的點,其x座標是已插入點最大/最小的。
情況2:插入的點,和之前插入的點,x座標相同(若新點y座標更小,直接拋棄,否則需要刪除曾經插入的點,來插入新的點)
還有一些情況,在寫map的時候回自己感覺到。。。如果寫 splay,就不會因為出現不好判斷(--map.begin())之類的情況的問題啦~ 其實寫其他平衡樹也行。
上**:很慢,最慢的3秒多。
#include #include #include #include #include #include #include using namespace std;
typedef long long ll;
int n, s;
const double inf = -1000000000000;
const int maxn = 100000 +100;
double a[maxn], b[maxn], r[maxn];
double f[maxn];
struct node
node(){}
};typedef map::iterator mit;
mapmp; //double表示那個點座標再x位置,node是那個點的各種資訊
mapxie; //表示first為斜率, second是x座標是x'的,和比x小的那個座標的斜率。
double xielv(node a, node b)
double getx(int k)//已知道f[k]的情況下, 求出x[k]
double gety(int k)
void init()
double get_f(int p)//條件充分,計算f[p]值
void del(double x)
void ins(double x, double y)
if (l_it == mp.begin() && xielv(it->second, l_it -> second) >= (l_it -> second).lk)
dont_delte_left:;
if (it == mp.begin()) //自己已經是最左邊界的情況
else
while (r_it != mp.end() && xielv(it-> second, r_it-> second) <= (r_it -> second).rk)
if (it == --mp.end())
else
xie[it->second.lk] = it-> second;
}void doit()
mp[100000000] = node(0, 100000000,0,0);
mp[inf] = node(100000000,0,0,0);
it = mp.upper_bound(x);
double kr = xielv(it -> second, node(x,y,0,0));
double kl = xielv((--it) -> second, node(x, y, 0, 0));
mp.erase(100000000);
mp.erase(inf);
if (kl < kr) //在凸殼內
ins(x, y);
} printf("%.3f\n", f[n]);
}int main()
return 0;
}
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...