CodeForces-372C Watching Fireworks is Fun
Watching Fireworks is Fun
dp
\(dp[i][j]\) 表示在第 \(i\) 个烟花,且此时位于第 \(j\) 个位置的最大快乐值
对于第 \(i\) 个烟花,此时处于第 \(j\) 个位置,可以从第 \(k\) 个位置走过来,因此有 \(k\) 的范围:
\[max(1, j - (t_i - t_{i-1}) * d) \leq k \leq min(n, j + (t_i - t_{i-1}) * d)
\]
有状态转移方程:\(dp[i][j] = \max_{k=max(1, j - (t_i - t_{i-1}) * d)}^{min(n, j + (t_i - t_{i-1}) * d}(dp[i][k] + b_i - |a_i - j|)\)
显然直接维护的话,转移 \(n\) 次的复杂度是 \(O(n^2)\)
但是我们可以考虑将方程的常数项提取出来,有转移方程:\(dp[i][j] = \max_{k=max(1, j - (t_i - t_{i-1}) * d)}^{min(n, j + (t_i - t_{i-1}) * d}(dp[i][k]) + b_i - |a_i - j|\)
可以看出转移方程就变成了求 \(dp[i-1][k]\) 的一个区间最大值,这个求若干连续区间最大值可以用单调队列来维护,这样转移 \(n\) 次的复杂度为 \(O(n)\)
#include <iostream>
#include <cstdio>
#include <deque>
using namespace std;
typedef long long ll;
#define pii pair<ll, ll>
const int maxn = 150010;
const ll inf = 0x3fffffffffffffff;
ll dp[2][maxn];
ll a[310], b[310], t[310];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
ll n, m, d;
cin >> n >> m >> d;
for(int i=1; i<=m; i++) cin >> a[i] >> b[i] >> t[i];
for(int i=1; i<=n; i++)
dp[0][i] = b[1] - abs(a[1] - i);
for(int i=2; i<=m; i++)
{
deque<pii>q;
ll k = 1;
for(int j=1; j<=n; j++)
{
ll lx = max(1ll, j - (t[i] - t[i-1]) * d);
ll rx = min(n, j + (t[i] - t[i-1]) * d);
while(k <= rx)
{
while(q.size() && dp[0][k] >= q.back().first) q.pop_back();
q.push_back({dp[0][k], k});
k++;
}
while(q.size() && q.front().second < lx) q.pop_front();
dp[1][j] = q.front().first + b[i] - abs(a[i] - j);
}
for(int i=0; i<=n; i++)
{
dp[0][i] = dp[1][i];
dp[1][i] = 0;
}
}
ll ans = -inf;
for(int i=1; i<=n; i++) ans = max(ans, dp[0][i]);
cout << ans << endl;
return 0;
}