神題啊,好吧,應該是因為我太弱了。。。
設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...