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 ;
}
发布了215 篇原创文章 · 获赞 12 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Irving0323/article/details/104216927
今日推荐