P15687 [ICPC 2023 Jakarta R] Maximize The Value 题解
思路
首先,考虑转化题意。假设现在有 \(m\) 操作,每一个操作会将变量 \(v\) 增加 \(a_i\),现在需要执行一段连续的操作区间,使得 \(v\) 最大。
其实这道题的本质就是上面说的那个子问题。对于每一个操作区间 \((l, r, x)\),如果当前 \(k \in [l, r]\) 中,那这个操作对 \(a_k\) 的影响就为 \(x\),否则就为 \(0\)。
这道题没有修改,很显然可以用一个扫描线把所有查询按 \(k\) 从小到大询问,这样就是线性的。考虑开一个 \([1, m]\) 的线段树,每一个叶子节点就代表第 \(i\) 个操作对于当前数的影响。那最后实际上就是求出 \([S, T]\) 中的最大子段和即可。这就是小白逛公园。最后用一个差分修改,如果 \(l_i = k\) 就把这个点的贡献加上,如果 \(r_i = k + 1\) 就把这个点的贡献减去。这样也就达成了 \((l, r, x)\) 的操作。时间复杂度 \(\mathcal{O}(m \log n)\)。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 5;
struct node
{
int k, x;
};
struct query
{
int k, l, r, id;
} q[N];
int n, m, t;
int ans[N];
vector<node> mo[N];
struct seg_tree
{
struct node
{
int l, r, sum, maxL, maxR, maxn;
} tr[4 * N];
node merge(node x, node y)
{
node res;
res.l = x.l, res.r = y.r;
res.sum = x.sum + y.sum;
res.maxL = max(x.maxL, x.sum + y.maxL);
res.maxR = max(y.maxR, y.sum + x.maxR);
res.maxn = max({x.maxn, y.maxn, x.maxR + y.maxL});
return res;
}
void push_up(int p)
{
tr[p] = merge(tr[p * 2], tr[p * 2 + 1]);
}
void build(int p, int l, int r)
{
tr[p] = {l, r, 0, 0, 0, 0};
if (l == r) return;
int mid = (l + r) >> 1;
build(p * 2, l, mid);
build(p * 2 + 1, mid + 1, r);
}
void modify(int p, int l, int r, int k, int x)
{
if (l == r)
{
tr[p].sum += x, tr[p].maxL += x, tr[p].maxR += x, tr[p].maxn += x;
return;
}
int mid = (l + r) >> 1;
if (k <= mid) modify(p * 2, l, mid, k, x);
else modify(p * 2 + 1, mid + 1, r, k, x);
push_up(p);
}
node query(int p, int l, int r)
{
if (l <= tr[p].l && tr[p].r <= r) return tr[p];
int mid = (tr[p].l + tr[p].r) >> 1;
if (r <= mid) return query(p * 2, l, r);
if (l > mid) return query(p * 2 + 1, l, r);
node res = merge(query(p * 2, l, r), query(p * 2 + 1, l, r));
return res;
}
} ST;
bool cmp(query x, query y)
{
return x.k < y.k;
}
signed main()
{
scanf("%lld%lld", &n, &m);
for (int i = 1, l, r, x; i <= m; i++)
{
scanf("%lld%lld%lld", &l, &r, &x);
mo[l].push_back({i, x});
mo[r + 1].push_back({i, -x});
}
scanf("%lld", &t);
for (int i = 1; i <= t; i++)
{
scanf("%lld%lld%lld", &q[i].k, &q[i].l, &q[i].r);
q[i].id = i;
}
sort(q + 1, q + t + 1, cmp);
ST.build(1, 1, m);
for (int i = 1, j = 1; i <= n; i++)
{
for (node now : mo[i])
ST.modify(1, 1, m, now.k, now.x);
while (j <= t && q[j].k == i)
{
int res = ST.query(1, q[j].l, q[j].r).maxn;
ans[q[j].id] = res;
j++;
}
}
for (int i = 1; i <= t; i++)
printf("%lld\n", ans[i]);
return 0;
}
写在最后:双倍经验 CF1906F。

浙公网安备 33010602011771号