將乙個長為\(n\)的序列分成\(k\)段,每段貢獻為其中不同數字的個數,求最大貢獻和。
此處感謝@gxy001 聚銠的精彩講解
先考慮暴力dp,可以想到乙個時空複雜度\(o(n^2k)\)的方法,即記錄前i個數字分成了j段。我們現在來思考幾個問題來優化這個操作:
對於乙個數字,它對那些地方實際有貢獻?
每次分割出乙個區間段對後續操作有影響的位置在哪?
每次轉移都從哪些地方繼承?
下來一一解答這些問題。
對於乙個數字,它能產生貢獻的區間其實就是該數字上一次出現的位置的後一位到它本身的位置。
對於每次劃分,它以前的位置的貢獻已經被考慮,所以我們只能考慮後面的位置。
相應的,每次轉移會繼承前面所有dp值的最大值。
那麼我們可以將k提出來,每次迴圈繼承上一次所有的dp值。因為只考慮從前面轉移dp值,所以不會對之前的決策產生影響,所以是正確的。
看看1、3問題的答案,是不是想到了rmq和區間賦值?
於是我們可以通過線段樹來實現dp優化。
具體來講,迭代k次,每次線段樹更新為上一次序列的dp值,然後從前往後掃,每個數會對其上述區間產生1的貢獻,轉移繼承前面所有dp值的最大值即可。
時間複雜度將一維優化為log。
#include#include#include#include#include#includeusing namespace std;
inline int read()
namespace star
e[maxn<<2];
inline void pushup(int ro)
inline void pushdown(int ro)
void build(int ro,int l,int r)
void rebuild(int tim,int ro)
rebuild(tim,ls);rebuild(tim,rs);
pushup(ro);
} void update(int ro,int x,int y)
pushdown(ro);
int mid=l+r>>1;
if(mid>=x)update(ls,x,y);
if(mid>1;
if(mid=y)return query(ls,x,y);
else return max(query(ls,x,mid),query(rs,mid+1,y));
} #undef ls
#undef rs
}t;inline void work()
printf("%d",f[n][k]); }}
signed main()
cf 833B 線段樹優化dp
寫出轉移方程即可。貢獻值計算有通用方法 記錄每乙個顏色上乙個位置,這個顏色只在上個位置到當前位置有貢獻,為1,這個可以通過線段樹做到。同樣,dp值也可以用線段樹查詢,所以我們把他們同時丟到一顆線段樹裡面就行了。include include include include include inclu...
cf474e Pillars 線段樹優化dp
有n個柱子,每個柱子有乙個高度hi h i,每個柱子可以跳到它後面高度與它相差大於d的柱子 即 h i hj d hi hj d 求最多可以跳多少個柱子an si m ax a nsj 1 hi h j d且 ja ns i ma x an sj 1 h i hj d且 j 因此建立一棵線段樹,節點...
線段樹優化dp
等離子炮有n個操作訊號,第i個操作訊號的強度為b i 總體強度為各操作訊號的強度之和。由於有些訊號太弱了了 強度 0 水寶寶想把它們刪除。但是水寶寶自己不會刪除訊號,所以他找來了同船的隊友幫忙。有 m位隊友,第ii 位隊友只會刪除編號在 l i 和 r i 之間的訊號,且每刪除乙個訊號,花費 c i...