E. Space Harbour
E. Space Harbour
There are $n$ points numbered $1$ to $n$ on a straight line. Initially, there are $m$ harbours. The $i$-th harbour is at point $X_i$ and has a value $V_i$. It is guaranteed that there are harbours at the points $1$ and $n$. There is exactly one ship on each of the $n$ 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 $0$.
Additionally, there are $q$ queries, each of which is either of the following $2$ types:
- $1$ $x$ $v$ — Add a harbour at point $x$ with value $v$. It is guaranteed that before adding the harbour, there is no harbour at point $x$.
- $2$ $l$ $r$ — Print the sum of the cost of moving all ships at points from $l$ to $r$ 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 $n$, $m$, and $q$ ($2 \le m \le n \le 3 \cdot 10^5$, $1 \le q \le 3 \cdot 10^5$) — the number of points, harbours, and queries, respectively.
The second line contains $m$ distinct integers $X_1, X_2, \ldots, X_m(1 \le X_i \le n)$ — the position at which the $i$-th harbour is located.
The third line contains $m$ integers $V_1, V_2, \ldots, V_m(1 \le V_i \le 10^7)$ — the value of the $i$-th harbour.
Each of the next $q$ lines contains three integers. The first integer is $t$ ($1\le t \le 2$) — type of query. If $t=1$, then the next two integers are $x$ and $v$ ($2 \le x \le n - 1$, $1 \le v \le 10^7$) — first-type query. If $t=2$, then the next two integers are $l$ and $r$ ($1 \le l \le r \le n$) — 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 $2$ query, the cost for ships at positions $2$, $3$, $4$ and $5$ are $3(3 \times 1)$, $0$, $96(24 \times 4)$ and $72(24 \times 3)$ respectively.
For the second type $2$ query, since the ship at position $5$ is already at a harbour, so the cost is $0$.
For the third type $2$ query, the cost for ships at position $7$ and $8$ are $15(15 \times 1)$ and $0$ respectively.
解题思路
初始时第 $i$ 个位置的代价为 $w_i = v_{l_i} \cdot (r_i - i)$,其中 $l_i$ 指左边最靠近 $i$ 的被标记的位置,$r_i$ 指右边最靠近 $i$ 的被标记的位置(用 std::set
维护已标记的位置)。当标记位置 $x$ 时,只有 $i \in [l_x + 1, r_x - 1]$ 的 $w_i$ 会受到影响。把这个范围分成两个部分来处理。
对于 $i \in [l_x + 1, x]$ 的位置,在标记了 $x$ 后,每一个 $i$ 右边被标记的位置变成了 $x$,即每一个 $w_i$ 都从原本的 $v_{l_i} \cdot (r_i - i)$ 变成 $v_{l_i} \cdot (x - i)$。因此需要对这个区间内的 $w_i$ 都加上定值 $-v_{l_i} \cdot (r_i - x)$。
对于 $i \in [x + 1, r_i - 1]$ 的位置,在标记了 $x$ 后,每一个 $i$ 左边被标记的位置变成了 $x$,即每一个 $w_i$ 都从原本的 $v_{l_i} \cdot (r_i - i)$ 变成 $v_{x} \cdot (r_i - i)$。因此需要对这个区间内的 $w_i$ 分别加上 $(v_x - v_{l_i}) \cdot (r_i - i)$,这个值会随着 $i$ 而变化。这相当于将一个首项为 $(v_x - v_{l_i}) \cdot (r_i - x - 1)$,公差为 $v_{l_i} - v_x$ 的等差数列加到区间 $[x + 1, r_i - 1]$ 的每个 $w_i$ 上。
为了统一处理,这里把对区间 $[l_x + 1, x]$ 的处理也看作是给区间加上首项为 $-v_{l_i} \cdot (r_i - x)$,公差为 $0$ 的等差数列。
区间加等差数列可以参考这两题:无聊的数列,牛牛的等差数列。需要用线段树来维护 $w_i$ 的差分数组,以及差分的二阶前缀和。
当给区间 $[l,r]$ 加上首项为 $k$,公差为 $d$ 的等差数列时,考虑对差分数组的影响。根据定义差分数组的 $g_i = w_i - w_{i-1}$,操作相当于给 $g_l$ 加上 $k$,$g_{l+1} \sim g_{r}$ 都加上 $d$,$g_{r+1}$ 加上 $-(k + (r-l) \cdot d)$。因此如果用线段树来维护差分数组,那么就可以很好的支持单点和区间修改,前缀 $[1,i]$ 的和就是 $w_i$。
但题目的询问是某个区间的 $w_i$,因此还需要维护关于 $g_i$ 的前缀和的前缀和,即二阶前缀和。如果要求 $w_i$ 的前缀 $[1,k]$ 的和:
\begin{align*}
\sum_{i=1}^{k}{w_i} =& \sum_{i=1}^{k}\sum_{j=1}^{i}{g_j} \\
=& g_1 + (g_1 + g_2) + (g_1 + g_2 + g_3) + \ldots + (g_1 + g_2 + \ldots + g_k) \\
=& k \cdot g_1 + (k-1) \cdot g_2 + \ldots + g_k \\
=& k \cdot (g_1 + g_2 + \cdots + g_k) - \left[ 0 \cdot g_1 + 1 \cdot g_2 + \ldots + (k-1) \cdot g_k \right] \\
=& k \cdot \sum_{i=1}^{k}{g_i} - \sum_{i=1}^{k}{(i-1)g_i}
\end{align*}
因此线段树除了需要维护差分数组 $g_i$ 的区间和 $s1$,还要维护 $(i-1)g_i$ 的区间和 $s2$。当需要给线段节点所维护区间 $[l,r]$ 的 $g_i$ 都加上 $c$,那么就有 $s_1 \gets s_1 + (r-l+1) \cdot c$,$s_2 \gets s2 + \frac{(r-l+1) \cdot (l+r-2)}{2} \cdot c$。
所以查询区间 $[l, r]$ 的和则需要先求出 $[1,r]$ 的和再减去 $[1, l-1]$ 的和。
AC 代码如下,时间复杂度为 $O(q \log{n})$:
#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