bzoj1835 線段樹優化dp)

2021-07-16 15:36:04 字數 1623 閱讀 5711

神題啊,好吧,應該是因為我太弱了。。。

設f[i][k]表示到第i個村莊,第i個村莊一定會建基站,已經建了k個基站的最小費用.

f[i][k]=min+c[i];

//注意本題線段樹維護的是當我們列舉i的時候,線段樹來維護f【j】【k-1】+cost【j】【i】,就是之前的值和中間的值,整體維護dp方程中的東西。同時在列舉i增加的過程中,維護cost【j】【i】(此時的i已經++了

cost(x,y)表示x到y這一段的最小補償費用.這個dp是o(n^3)的,tle.

由於狀態數已經是n^2,主要的瓶頸在於轉移的複雜度cost(x,y)的計算.

考慮y增加對solve(x,y)的影響.

原來被左端點覆蓋的沒有影響,

被右端點覆蓋的會減少並且不會再被覆蓋.

考慮用線段樹優化這個dp.用線段樹維護f[x]+

cost(x+1,y)的最小值.

設bg[x],ed[x]表示在[bg[x],ed[x]]範圍內建造基站村莊x能被覆蓋.

因為a村莊已經無法被右端點覆蓋,所以這些當做左端點也無法覆蓋a的村莊的f+cost值肯定要增加w[a];

列舉k,每次都要重新建樹.我們可以把n和k加1,這樣每次最優值都儲存在f[n]中

實際上本題的本質就是,寫出dp方程,用線段樹來維護dp中的資料,並且高效的進行區間更新,查詢最值

這道題思路是並不難理解,但是**實現確實有一些技巧,剛開始我就寫不出來啊,細節詳見注釋

#include#include#include#include#includeusing namespace std;

typedef long long ll;

const int inf=0x3f3f3f3f;

int n,k;

ll f[200005],dis[200005],c[200005],w[200005],s[200005];

int ed[200005],bg[200005];

int tot,head[200005],pre[200005],to[200005];

void addedge(int x,int y)

struct aa

a[200005<<2];

void up(int i)

void down(int i)

}void build(int i,int l,int r)

int mid=(l+r)>>1;

build(i<<1,l,mid);

build(i<<1|1,mid+1,r);

up(i);

}void add(int i,int l,int r,ll x)

down(i);

int mid=(a[i].l+a[i].r)>>1;

if (mid>=r) add(i<<1,l,r,x);

else if (midr) return 0;

if (a[i].l==l&&a[i].r==r) return a[i].mi;

int mid=(a[i].l+a[i].r)>>1;

down(i);

if (mid>=r) return query(i<<1,l,r);

else if (mid

線段樹優化dp

等離子炮有n個操作訊號,第i個操作訊號的強度為b i 總體強度為各操作訊號的強度之和。由於有些訊號太弱了了 強度 0 水寶寶想把它們刪除。但是水寶寶自己不會刪除訊號,所以他找來了同船的隊友幫忙。有 m位隊友,第ii 位隊友只會刪除編號在 l i 和 r i 之間的訊號,且每刪除乙個訊號,花費 c i...

降臨(線段樹優化dp)

選定點i會有代價 c i 如果乙個區間j內的點全被選擇,就可以獲得回報 p j 點數和區間個數 1e5 還以為是線段樹優化網路流 50萬個點200萬條邊看上去很可做的樣子畢竟lbn說過網路流20萬萬條邊完全沒問題 沒想到是個線段樹dp。雖然這兩個線段樹完全扯不上關係 用 f i j 表示考慮到第i個...

線段樹優化DP之Monotonicity

定義f i 為處理到第i位,所得匹配的最長長度,根據f i 我們可以求出它後面要跟的符號 可以用符號填滿,避免一些取模運算 對於i,我們列舉每乙個i前面的j,判斷是否合法,那麼 n 2 的做法就可以寫出來了 includeusing namespace std const int maxn 2000...