降臨(線段樹優化dp)
阿新 • • 發佈:2018-07-28
main spa space odi pri line 除了 發現 獲得 。
降臨
選定點i會有代價\(c_i\),如果一個區間j內的點全被選擇,就可以獲得回報\(p_j\)。點數和區間個數\(<1e5\)。
還以為是線段樹優化網絡流(50萬個點200萬條邊看上去很可做的樣子畢竟lbn說過網絡流20萬萬條邊完全沒問題),沒想到是個線段樹dp。
(雖然這兩個線段樹完全扯不上關系)
用\(f[i][j]\)表示考慮到第i個點,向左最近的尚未選定的點為j時的最大值。那麽,i+1可以選也可以不選。不選i時,\(f[i][j]\rightarrow f[i+1][i+1]\)。選i時,那麽i和左邊選過的連續點可以連成區間。則\(f[i][j]-c[i+1]+\sum_{l[k]>j,r[k]=i+1}p_k\rightarrow f[i+1][j]\)
我們發現,除了i+1,j只會轉移到j。因此,考慮使用線段樹維護\(f[i]\)中狀態的最大值。轉移時,找出所有以i+1為右端點的區間,將他們按照左端點排序,對左端點之間形成的區間統一在線段樹上更新即可。
#include <vector> #include <set> #include <cstdio> #include <algorithm> using namespace std; typedef long long ll; typedef pair<ll, ll> pi; const int maxn=2e6+5; ll n,m,x,y,z; ll seg[maxn],add[maxn],c[maxn]; vector<pi> vec[maxn]; void push(ll x){ if (add[x]){ seg[x<<1]+=add[x]; seg[x<<1|1]+=add[x]; add[x<<1]+=add[x]; add[x<<1|1]+=add[x]; add[x]=0; } } ll query(ll x,ll l,ll r,ll L,ll R){ if (L<=l&&R>=r) return seg[x]; push(x); ll z=0; ll mid=(l+r)>>1; if (L<=mid) z=max(z, query(x<<1, l, mid, L, R)); if (R>mid) z=max(z, query(x<<1|1, mid+1, r, L, R)); return z; } void modify(ll x,ll l,ll r,ll L,ll R,ll v){ if (L<=l&&R>=r){ add[x]+=v; seg[x]+=v; return; } push(x); ll mid=(l+r)>>1; if (L<=mid) modify(x<<1, l, mid, L, R, v); if (R>mid) modify(x<<1|1, mid+1, r, L, R, v); seg[x]=max(seg[x<<1], seg[x<<1|1]); } int main(){ scanf("%lld%lld", &n, &m); for (ll i=1; i<=n; i++) scanf("%lld", &c[i]); for (ll i=1; i<=m; i++){ scanf("%lld%lld%lld", &x, &y, &z); vec[y].push_back(make_pair(x,z)); } for (ll i=1; i<=n; i++){ modify(1,0,n,i,i,query(1,0,n,0,i-1)); //j=i表示不選i for (ll j=0; j<=(ll)vec[i].size()-1; j++) //加上區間價值 modify(1,0,n,0,vec[i][j].first-1,vec[i][j].second); modify(1,0,n,0,i-1,-c[i]); //j!=i表示選i,需要減去c[i] } printf("%lld\n", seg[1]); return 0; }
降臨(線段樹優化dp)