题目
题目描述
译自 JOI 2020 Final T5「火事 / Fire」
在 JOI 世界里有 个地区排成一条线。为了方便,我们将这些地区编号为 到 。突然,各个地区都起火了。在时刻 ,第 个区的火势大小为 。
此时(时刻 ),一阵风从 号地区一直吹到了 号地区。对于每两个相邻的地区,如果 时刻上风地区的火势比下风地区的强, 时刻下风地区的火势大小将变为 时刻上风地区的火势,否则 和 时刻时下风地区的火势大小不变。
形式化地说,如果 时刻 地区的火势为 ,则 ,其中 。
你是一位消防员。现在,你想到了 种灭火方案,并打算执行其中一种。你的第 种方案是在 时刻对 中的所有地区使用灭火剂完全扑灭火灾。
对于一个火势大小为 的城市,你将需要 升的灭火剂来扑灭火灾。因此,执行方案 总共要花费 升灭火剂。
译者注:英文版题面中没有定义 ,此处定义从日文版中提取。
为了更好地选取灭火方案,你的任务是编写一个程序,给出 时刻的火势大小,计算各个方案所需的灭火剂量。
输入格式
第一行两个数 ,含义如题面所示。
接下来一行 个数 ,表示初始时的火势大小。
接下来 行每行三个数 ,表示方案 的相关信息。
输出格式
输出 行,第 行表示方案 所需的灭火剂量。
样例
样例输入 1
5 5
9 3 2 6 5
1 1 3
2 1 5
3 2 5
4 3 3
5 3 5
样例输出 1
21
39
33
9
27
样例解释 1
时刻 时地区 到 地区 的火势大小分别为 。
时刻 时地区 到 地区 的火势大小分别为 。方案 需要的灭火剂量为 升。
时刻 时地区 到 地区 的火势大小分别为 。方案 需要的灭火剂量为 升。
时刻 时地区 到 地区 的火势大小分别为 。方案 需要的灭火剂量为 升。
时刻 时地区 到 地区 的火势大小分别为 。方案 需要的灭火剂量为 升。
时刻 时地区 到 地区 的火势大小分别为 。方案 需要的灭火剂量为 升。
该样例满足子任务 和子任务 的限制。
样例输入 2
10 10
3 1 4 1 5 9 2 6 5 3
1 1 6
2 8 10
4 2 7
8 3 3
6 1 10
3 2 8
5 1 9
7 4 5
9 7 9
10 10 10
样例输出2
28
21
34
4
64
43
55
9
27
9
样例解释 2
该样例满足子任务 和子任务 的限制。
样例输入 3
10 10 3
1 4 1 5 9 2 6 5 3
1 6 6
2 8 8
4 2 2
8 3 3
6 1 1
3 4 4
5 5 5
7 10 10
9 8 8
10 7 7
样例输出 3
9
9
3
4
3
4
5
9
9
9
样例解释 3
该样例满足子任务 的限制。
样例输入 4
10 10
3 1 4 1 5 9 2 6 5 3
7 1 6
7 8 10
7 2 7
7 3 3
7 1 10
7 2 8
7 1 9
7 4 5
7 7 9
7 10 10
样例输出 4
28
27
34
4
64
43
55
9
27
9
样例解释 4
该样例满足子任务 的限制。
样例输入 5
20 20
2 1 2 2 1 1 1 1 2 2 2 1 2 1 1 2 1 2 1 1
1 1 14
2 3 18
4 10 15
8 2 17
9 20 20
4 8 19
7 2 20
11 1 5
13 2 8
20 1 20
2 12 15
7 1 14
12 7 18
14 2 17
9 19 20
12 12 12
6 2 15
11 2 15
19 12 17
4 1 20
样例输出 5
25
30
12
32
2
24
38
10
14
40
8
28
24
32
4
2
28
28
12
40
样例解释 5
该样例满足子任务 的限制。
数据范围与提示
对于所有测试数据 。
子任务编号 分值 具体限制
思路
考虑连续相同的缩成一段,记f[i]表示第i段的值是否大于第i+1段的值
直到最后一个0那段长度变为0,需要把这一段删除,那么当前状态就会发生改变。
考虑用平衡树维护,每一段的长度是和当前时间有关的一个一次函数,和也是,那么只要维护这两个的子树和,就可以查询答案。
在每个会发生状态改变的时间打tag,因为只会涉及最近的几段,所以讨论一下即可。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 77;
struct fenwick_tree {
long long c[N + N];
void modify(int k, long long v) {
while (k < N + N) c[k] += v, k += k & -k;
}
long long query(int k) {
long long res = 0;
while (k) res += c[k], k ^= k & -k;
return res;
}
};
struct data_structure {
fenwick_tree T1, T2;
void modify(int k, int v) {
k += 2e5;
T1.modify(1, v);
T1.modify(k + 1, -v);
T2.modify(k + 1, 1ll * k * v);
}
long long query(int k) {
k += 2e5;
return T1.query(k) * k + T2.query(k);
}
} A, B;
int n, q, a[N], s[N], top, L[N], R[N];
long long sum[N], ans[N];
vector<pair<int, int> > mdf[N], qry[N];
int main() {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
sum[i] = sum[i - 1] + a[i];
}
for (int i = 1; i <= n; ++i) {
while (top && a[i] >= a[s[top]]) R[s[top--]] = i;
L[i] = s[top];
s[++top] = i;
}
for (int i = 1; i <= n; ++i)
if (L[i]) {
mdf[i - L[i]].push_back({ i, a[L[i]] - a[i] });
if (R[i])
mdf[R[i] - L[i]].push_back({ R[i], a[i] - a[L[i]] });
}
for (int i = 1, t, l, r; i <= q; ++i) {
scanf("%d%d%d", &t, &l, &r);
ans[i] = sum[r] - sum[l - 1];
qry[t].push_back({ r, i });
qry[t].push_back({ l - 1, -i });
}
for (int i = 1; i <= n; ++i) {
for (auto x : mdf[i]) {
A.modify(x.first - i, x.second);
B.modify(x.first - 1, x.second);
}
for (auto x : qry[i])
if (x.second > 0)
ans[x.second] += A.query(x.first - i) - B.query(x.first);
else
ans[-x.second] -= A.query(x.first - i) - B.query(x.first);
}
for (int i = 1; i <= q; ++i) printf("%lld\n", ans[i]);
return 0;
}