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 ;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]中提到了一种树形动态规划的方法来解决CodeForces - 982C问。在这个问中,subtree指的是子连通块,而不是子树。为了使cnt_white - cnt_black尽可能大,可以使用两次树形动态规划来求解。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。如果ans\[u\]为正,则减去dp\[v\]就是树链所代表的子树的权值。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] 问: CodeForces - 982C 树形DP是什么问?如何解决? 回答: CodeForces - 982C是一个树形动态规划问。在这个问中,需要求解子连通块的最大权值和,使得cnt_white - cnt_black尽可能大。解决这个问的方法是使用两次树形动态规划。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] #### 引用[.reference_title] - *1* *2* [CodeForces - 1324F Maximum White Subtree(树形dp)](https://blog.csdn.net/qq_45458915/article/details/104831678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值