【JZOJ 杂题选讲】火灾

题目

题目描述
译自 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值