题目传送门
题意:
在一维数轴上有n个位置,依次摆放在[1 , n]区间的整数点上,每个位置都不重合。
现在有m个烟花,每个烟花会在 时刻 位置绽放。
假如第 个烟花绽放时,当时你在 位置,那么你收获的幸福值是 。 表示 的绝对值。
每个单位时间,你可以选择移动[0 , d]个位置。
初始位置你自己任意设置,允许多个烟花同时绽放。
问你m个烟花绽放后,你的幸福值最大是多少。
数据范围: , ,
, , 。
题解:
大概想想就知道是dp题。
状态: 表示第 个烟花绽放时你在 这个位置时前 个烟花绽放的最大幸福值的和。
转移方程: 。
这个方程的 是有范围的,这个范围是你在两个烟花绽放之间的时间内能走的步数。
因此 的范围是 。
时间复杂度是 ,这个复杂度接受不了。
肯定是要优化的。
以往寻找 时,我会用线段树做,那么这个题的复杂度就是 。
这道题用线段树找最值也许能过,我没试。但可以更快。
我们每次找等长区间的最值,那么可以用单调队列去优化。
我们在计算 到 的状态时,我们去维护 到 的单调队列,去获得我们想要的等长区间最大值。
时间复杂度是 。
感受:
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 ;
}