D. Optimal Partition_線段樹優化DP
阿新 • • 發佈:2022-04-22
D. Optimal Partition
題目大意:
給一個數組a,現在要將其分成若干個子段。
若該段和大於0則價值加上段長度;
若該段和等於0則價值不變;
若該段和小於0則價值減去段長度;
問最大價值。
思路和程式碼:
賽時這題沒什麼思路,甚至沒想到dp...
看到呆瓜隊友交了幾發居然在貪...
首先這是一個dp優化,很對我胃口~
不難想到O(n2)的dp:(其中p為字首和)
稍做整理後可以得到:
於是我們可以將p作為線段樹的“x軸”,維護{dpj-j,dpj,dpj+j}三棵線段樹。這樣的話就可以將複雜度降低到O(nlogn)。
當然,p陣列範圍太大,不可能直接作為線段樹的葉子節點,所以我們可以先做一次離散化
ll p[N] ; struct Node{ int l , r ; ll val ; }tr[N << 2][3]; // 0 1 2 // + 0 - void build(int now , int l , int r){// cout << l << " " << r << "\n" ; tr[now][k] = {l , r , -INF} ; if(l == r){ tr[now][k].val = -INF ; return ; } int mid = l + r >> 1 ; build(now << 1 , l , mid) ; build(now << 1 | 1 , mid + 1 , r) ; tr[now][k].val = max(tr[now << 1][k].val , tr[now << 1 | 1][k].val) ; } ll query(int now , int l , int r){ if(l <= tr[now][k].l && tr[now][k].r <= r) return tr[now][k].val ; int mid = tr[now][k].l + tr[now][k].r >> 1 ; ll res = -INF ; if(l <= mid) res = max(res , query(now << 1 , l , r)) ; if(mid < r ) res = max(res , query(now << 1 | 1 , l , r)) ; return res ; } void modify(int now , int x , ll data){ if(tr[now][k].l == tr[now][k].r) { tr[now][k].val = max(tr[now][k].val , data) ; return ; } int mid = tr[now][k].l + tr[now][k].r >> 1 ; if(x <= mid) modify(now << 1 , x , data) ; else modify(now << 1 | 1 , x , data) ; tr[now][k].val = max(tr[now << 1][k].val , tr[now << 1 | 1][k].val) ; } void solve(){ cin >> n ; rep(i , 1 , n){ ll tmp ; cin >> tmp ; p[i] = p[i - 1] + tmp ; } vct<ll> a ; a.pb(0) ; rep(i , 1 , n) a.pb(p[i]) ; sort(a.begin() , a.end()) ; a.erase(unique(a.begin() , a.end()) , a.end()) ; vct<int> idx(n + 1 , 0) ; rep(i , 0 , n) idx[i] = lower_bound(a.begin() , a.end() , p[i]) - a.begin() + 1 ; rep(i , 1 , 3){ k = i - 1 ; build(1 , 1 , n + 1) ; modify(1 , idx[0] , 0) ; } vct<ll> dp(n + 1 , -INF) ; for(ll i = 1 ; i <= n ; i ++ ){ ll now = idx[i] ; k = 0 ; if(now > 1) dp[i] = max(dp[i] , query(1 , 1 , now - 1) + i) ; k = 1 ; dp[i] = max(dp[i] , query(1 , now , now)) ; k = 2 ; if(now + 1 <= n + 1) dp[i] = max(dp[i] , query(1 , now + 1 , n + 1) - i) ; k = 0 ; modify(1 , now , dp[i] - i) ; k = 1 ; modify(1 , now , dp[i]) ; k = 2 ; modify(1 , now , dp[i] + i) ; } cout << dp[n] << "\n" ; }//code_by_tyrii
小結:
不太懂為什麼要特別把0加入離散化中(雖然我知道這樣確實是對的)
這題有點離譜哦,不會做先不說,做出來的也差別好多。
用線段樹的和用樹狀陣列的就差好多好多(畢竟區間查詢單點修改)
研究一下樹狀陣列做法再水一篇咯~