1. 程式人生 > 其它 >D. Optimal Partition_線段樹優化DP

D. Optimal Partition_線段樹優化DP

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加入離散化中(雖然我知道這樣確實是對的)

這題有點離譜哦,不會做先不說,做出來的也差別好多。

用線段樹的和用樹狀陣列的就差好多好多(畢竟區間查詢單點修改)

研究一下樹狀陣列做法再水一篇咯~