E. Space Harbour
E. Space Harbour
There are points numbered to on a straight line. Initially, there are harbours. The -th harbour is at point and has a value . It is guaranteed that there are harbours at the points and . There is exactly one ship on each of the points. The cost of moving a ship from its current location to the next harbour is the product of the value of the nearest harbour to its left and the distance from the nearest harbour to its right. Specifically, if a ship is already at a harbour, the cost of moving it to the next harbour is .
Additionally, there are queries, each of which is either of the following types:
- — Add a harbour at point with value . It is guaranteed that before adding the harbour, there is no harbour at point .
- — Print the sum of the cost of moving all ships at points from to to their next harbours. Note that you just need to calculate the cost of moving the ships but not actually move them.
Input
The first line contains three integers , , and (, ) — the number of points, harbours, and queries, respectively.
The second line contains distinct integers — the position at which the -th harbour is located.
The third line contains integers — the value of the -th harbour.
Each of the next lines contains three integers. The first integer is () — type of query. If , then the next two integers are and (, ) — first-type query. If , then the next two integers are and () — second-type query.
It is guaranteed that there is at least one second-type query.
Output
For every second-type query, print one integer in a new line — answer to this query.
Example
input
8 3 4
1 3 8
3 24 10
2 2 5
1 5 15
2 5 5
2 7 8
output
171
0
15
Note
For the first type query, the cost for ships at positions , , and are , , and respectively.
For the second type query, since the ship at position is already at a harbour, so the cost is .
For the third type query, the cost for ships at position and are and respectively.
解题思路
初始时第 个位置的代价为 ,其中 指左边最靠近 的被标记的位置, 指右边最靠近 的被标记的位置(用 std::set
维护已标记的位置)。当标记位置 时,只有 的 会受到影响。把这个范围分成两个部分来处理。
对于 的位置,在标记了 后,每一个 右边被标记的位置变成了 ,即每一个 都从原本的 变成 。因此需要对这个区间内的 都加上定值 。
对于 的位置,在标记了 后,每一个 左边被标记的位置变成了 ,即每一个 都从原本的 变成 。因此需要对这个区间内的 分别加上 ,这个值会随着 而变化。这相当于将一个首项为 ,公差为 的等差数列加到区间 的每个 上。
为了统一处理,这里把对区间 的处理也看作是给区间加上首项为 ,公差为 的等差数列。
区间加等差数列可以参考这两题:无聊的数列,牛牛的等差数列。需要用线段树来维护 的差分数组,以及差分的二阶前缀和。
当给区间 加上首项为 ,公差为 的等差数列时,考虑对差分数组的影响。根据定义差分数组的 ,操作相当于给 加上 , 都加上 , 加上 。因此如果用线段树来维护差分数组,那么就可以很好的支持单点和区间修改,前缀 的和就是 。
但题目的询问是某个区间的 ,因此还需要维护关于 的前缀和的前缀和,即二阶前缀和。如果要求 的前缀 的和:
因此线段树除了需要维护差分数组 的区间和 ,还要维护 的区间和 。当需要给线段节点所维护区间 的 都加上 ,那么就有 ,。
所以查询区间 的和则需要先求出 的和再减去 的和。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 3e5 + 10;
int a[N], b[N];
LL w[N];
set<int> st;
struct Node {
int l, r;
LL s1, s2, add;
}tr[N * 4];
void pushup(int u) {
tr[u].s1 = tr[u << 1].s1 + tr[u << 1 | 1].s1;
tr[u].s2 = tr[u << 1].s2 + tr[u << 1 | 1].s2;
}
void build(int u, int l, int r) {
tr[u] = {l, r, 0, 0, 0};
if (l == r) {
tr[u].s1 = w[l] - w[l - 1];
tr[u].s2 = tr[u].s1 * (l - 1);
}
else {
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void upd(int u, LL c) {
int len = tr[u].r - tr[u].l + 1;
tr[u].s1 += c * len;
tr[u].s2 += c * len * (tr[u].l + tr[u].r - 2) / 2;
tr[u].add += c;
}
void pushdown(int u) {
if (tr[u].add) {
upd(u << 1, tr[u].add);
upd(u << 1 | 1, tr[u].add);
tr[u].add = 0;
}
}
void modify(int u, int l, int r, LL c) {
if (tr[u].l >= l && tr[u].r <= r) {
upd(u, c);
}
else {
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) modify(u << 1, l, r, c);
if (r >= mid + 1) modify(u << 1 | 1, l, r, c);
pushup(u);
}
}
LL query(int u, int l, int r) {
if (l > r) return 0;
if (tr[u].l >= l && tr[u].r <= r) return r * tr[u].s1 - tr[u].s2;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
LL ret = 0;
if (l <= mid) ret = query(u << 1, l, r);
if (r >= mid + 1) ret += query(u << 1 | 1, l, r);
return ret;
}
void add(int l, int r, LL k, LL d) {
if (l > r) return;
modify(1, l, l, k);
modify(1, l + 1, r, d);
modify(1, r + 1, r + 1, -(k + (r - l) * d));
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, k;
cin >> n >> m >> k;
for (int i = 1; i <= m; i++) {
cin >> a[i];
st.insert(a[i]);
}
for (int i = 1; i <= m; i++) {
cin >> b[a[i]];
}
for (int i = 1; i <= n; i++) {
int l = *prev(st.upper_bound(i)), r = *st.lower_bound(i);
w[i] = 1ll * b[l] * (r - i);
}
build(1, 1, n);
while (k--) {
int op, x, y;
cin >> op >> x >> y;
if (op == 1) {
int l = *prev(st.upper_bound(x)), r = *st.lower_bound(x);
add(l + 1, x, -1ll * b[l] * (r - x), 0);
add(x + 1, r - 1, (y - b[l]) * (r - x - 1ll), b[l] - y);
st.insert(x);
b[x] = y;
}
else {
cout << query(1, 1, y) - query(1, 1, x - 1) << '\n';
}
}
return 0;
}
参考资料
Codeforces Round 921 (Div. 1, Div. 2) Editorial:https://codeforces.com/blog/entry/125137
Educational Codeforces Round 126 (Rated for Div. 2)-D. Progressions Covering(区间加等差数列/思维):https://blog.csdn.net/m0_51979134/article/details/124129258
牛客挑战赛39 牛牛的等差数列 (线段树 / 树状数组) (等差数列 高阶差分 高阶前缀和):https://blog.csdn.net/weixin_45799835/article/details/120820519
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18014513
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2023-02-13 D. Moscow Gorillas
2023-02-13 B. Fedya and Array
2022-02-13 飞行员兄弟