題目傳送門
題意:
在一維數軸上有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 ;
}