codeforces372C 2400分dp + 單調隊列優化

題目傳送門

題意:

在一維數軸上有n個位置,依次擺放在[1 , n]區間的整數點上,每個位置都不重合。

現在有m個煙花,每個煙花會在 \dpi{150}t_i 時刻 a_i 位置綻放。

假如第 i 個煙花綻放時,當時你在 j 位置,那麼你收穫的幸福值是 b_i-\left | a_i - j \right | 。 \left | x \right | 表示 x 的絕對值。

每個單位時間,你可以選擇移動[0 , d]個位置。

初始位置你自己任意設置,允許多個煙花同時綻放。

問你m個煙花綻放後,你的幸福值最大是多少。

數據範圍:1 \leqslant n\leqslant 150000 , 1 \leqslant m \leqslant 300 , 

1 \leqslant d , ai \leqslant n , 1 \leqslant b_i , t_i \leqslant 10^9 ,  t_i \leqslant t_{i+1} (1\leqslant i<m)

題解:

大概想想就知道是dp題。

狀態: dp[i][j]  表示第 i 個煙花綻放時你在 j 這個位置時前 i 個煙花綻放的最大幸福值的和。

轉移方程: dp[i][j] = max(dp[i-1][k]) + b[i] - \left | a_i - j \right |  。

這個方程的 k 是有範圍的,這個範圍是你在兩個煙花綻放之間的時間內能走的步數。

因此 k 的範圍是 j - (t_i - t_{i-1})*d \leqslant k \leqslant j + (t_i - t_{i-1})*d 。

時間複雜度是  O(m*n^2) ,這個複雜度接受不了。

肯定是要優化的。

以往尋找 max(dp[i-1][k]) 時,我會用線段樹做,那麼這個題的複雜度就是 O(m*n*logn)

這道題用線段樹找最值也許能過,我沒試。但可以更快。

我們每次找等長區間的最值,那麼可以用單調隊列去優化。

我們在計算 dp[i][1] 到 dp[i][n] 的狀態時,我們去維護 dp[i-1][1] 到 dp[i-1][n] 的單調隊列,去獲得我們想要的等長區間最大值。

時間複雜度是 O(n*m) 。

感受:

bug沒調出來,稍微換了一下寫法就做過了,不知道爲什麼。

cf的題稍微加個算法分就很高了,不知道這題爲什麼有2400分。

感覺是結合比賽情況的考慮,並不單純是題目難度。單純題目難度的話,這題絕對沒有2400分。

代碼: 

#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
typedef pair<int , int> pii ;
const int maxn = 1e5 + 5e4 + 5 ;
const int maxm = 305 ;
int n , m , d ;
ll a[maxm] , b[maxm] , t[maxm] ;
ll dp[2][maxn] ;
deque<int> q ;
int main()
{
   scanf("%d%d%d" , &n , &m , &d) ;
   for(int i = 1 ; i <= m ; i ++)  
     scanf("%lld%lld%lld" , &a[i] , &b[i] , &t[i]) ;	
   for(int i = 1 ; i <= m ; i ++)
   {
   	 ll r = 0 ;
   	 int now = i % 2 , last = 1 - now ;
   	 while(!q.empty())  q.pop_front() ;
   	 for(int j = 1 ; j <= n ; j ++)
   	 {
   	 	ll step = (t[i] - t[i - 1]) * d ;
   	    while(!q.empty() && q.front() < j - step)  q.pop_front() ;
		while(r < n && r < j + step)
		{
			r ++ ;
		   	while(!q.empty() && dp[last][q.back()] < dp[last][r])
			  q.pop_back() ;
			q.push_back(r) ;   
		}
		dp[now][j] = dp[last][q.front()] + b[i] - abs(a[i] - j) ; 	
	    //cout << i << ' ' << j << '\n' ;
	 }  
   }
   ll ans = -1e18 ;
   for(int i = 1 ; i <= n ; i ++)  ans = max(ans , dp[m % 2][i]) ;
   printf("%lld\n" , ans) ;
   return 0 ;
}

 

發佈了231 篇原創文章 · 獲贊 12 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章