Loading

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;
}
posted @ 2022-07-14 22:17  dgsvygd  阅读(24)  评论(0编辑  收藏  举报