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;
}