P9871 [NOIP2023] 天天爱打卡 题解

先考虑 $n\le 10^5$。

设 $f_i$ 表示钦定第 $i$ 天不跑步,前 $i$ 天结束后能量值最高可以达到多少,

枚举上一次不跑步是在哪天,则有转移 $f_i=\max\limits_{i-j-1\le k}f_j+v(j+1,i-1)-d(i-j-1)$,

其中 $v(l,r)$ 表示被 $[l,r]$ 包含的挑战的 $v_i$ 之和。

线段树维护 $g_j=f_j+v(j+1,i-1)+dj$,则 $f_i=-d(i-1)+\max\limits_{i-j-1\le k}g_j$。

考虑如何更新 $g_j$。显然求出 $f_i$ 后要更新 $g_i\gets f_i+di$,

而 $i\gets i+1$ 时,每个 $g_j$ 上的 $v(j+1,i-1)$ 一项变为 $v(j+1,i)$,

若 $i$ 是某个挑战 $x$ 的右端点,则 $\forall j<l_x$,它们的 $g_j$ 都要加上 $v_x$。

维护出 $g$ 就可以维护出 $f$ 了,答案即为 $f_{n+1}$。

再考虑 $n\le 10^9$。

可以发现挑战把 $[1,n]$ 分成 $O(m)$ 段,把上面的“天”全部改成“段”即可。

#include <cstdio>
#include <vector>
#include <algorithm>
#define int long long
using namespace std;
struct S
{
    int l, r, v;
}a[200050];
struct T
{
    int v, z;
}R[200050 << 2];
void u(int p) {R[p].v = max(R[p << 1].v, R[p << 1 | 1].v);}
void f(int p, int x) {R[p].v += x, R[p].z += x;}
void d(int p) {if(R[p].z) f(p << 1, R[p].z), f(p << 1 | 1, R[p].z), R[p].z = 0;}
void M(int l, int r, int x, int s, int t, int p)
{
    if(l <= s && t <= r)
        return f(p, x);
    d(p);
    int m = s + t >> 1;
    if(l <= m)
        M(l, r, x, s, m, p << 1);
    if(r > m)
        M(l, r, x, m + 1, t, p << 1 | 1);
    u(p);
}
int Q(int l, int r, int s, int t, int p)
{
    if(l <= s && t <= r)
        return R[p].v;
    d(p);
    int m = s + t >> 1, q = -1e18;
    if(l <= m)
        q = max(q, Q(l, r, s, m, p << 1));
    if(r > m)
        q = max(q, Q(l, r, m + 1, t, p << 1 | 1));
    return q;
}
void D(int s, int t, int p)
{
    R[p].v = R[p].z = 0;
    if(s == t) return;
    int m = s + t >> 1;
    D(s, m, p << 1);
    D(m + 1, t, p << 1 | 1);
}
int T, n, m, k, w, z, F[200050], v[200050];
vector<int> V[200050];
signed main()
{
    scanf("%*lld%lld", &T);
    while(T--)
    {
        scanf("%lld%lld%lld%lld", &n, &m, &k, &w);
        v[z++] = 0;
        for(int i = 1;i <= m;++i)
            scanf("%lld%lld%lld", &a[i].r, &a[i].l, &a[i].v), a[i].l = a[i].r - a[i].l + 1, v[z++] = a[i].r, v[z++] = a[i].l - 1;
        sort(v, v + z);
        z = unique(v, v + z) - v;
        for(int i = 1;i <= m;++i)
            V[lower_bound(v, v + z, a[i].r) - v].push_back(i);
        for(int i = 1;i <= z;++i)
        {
            F[i] = Q(lower_bound(v, v + z, v[i - 1] - k) - v, i - 1, 0, z, 1) - w * v[i - 1];
            M(i, i, F[i] + w * v[i], 0, z, 1);
            for(auto j : V[i])
                M(0, lower_bound(v, v + z, a[j].l - 1) - v, a[j].v, 0, z, 1);
        }
        printf("%lld\n", F[z]);
        D(0, z, 1);
        for(int i = 0;i <= z;++i)
            F[i] = v[i] = 0, V[i].clear();
        z = 0;
    }
    return 0;
}
posted @ 2023-11-29 16:40  5k_sync_closer  阅读(33)  评论(0编辑  收藏  举报  来源