G2. Yunli's Subarray Queries (hard version)
This is the hard version of the problem. In this version, it is guaranteed that $r \geq l+k-1$ for all queries.
For an arbitrary array $b$, Yunli can perform the following operation any number of times:
- Select an index $i$. Set $b_i = x$ where $x$ is any integer she desires ($x$ is not limited to the interval $[1,n]$).
Denote $f(b)$ as the minimum number of operations she needs to perform until there exists a consecutive subarray$^{\text{∗}}$ of length at least $k$ in $b$.
Yunli is given an array $a$ of size $n$ and asks you $q$ queries. In each query, you must output $\sum_{j=l+k-1}^{r} f([a_l, a_{l+1}, \ldots, a_j])$.
$^{\text{∗}}$If there exists a consecutive subarray of length $k$ that starts at index $i$ ($1 \leq i \leq |b|-k+1$), then $b_j = b_{j-1} + 1$ for all $i < j \leq i+k-1$.
Input
The first line contains $t$ ($1 \leq t \leq 10^4$) — the number of test cases.
The first line of each test case contains three integers $n$, $k$, and $q$ ($1 \leq k \leq n \leq 2 \cdot 10^5$, $1 \leq q \leq 2 \cdot 10^5$) — the length of the array, the length of the consecutive subarray, and the number of queries.
The following line contains $n$ integers $a_1, a_2, ..., a_n$ ($1 \leq a_i \leq n$).
The following $q$ lines contain two integers $l$ and $r$ ($1 \leq l \leq r \leq n$, $r \geq l+k-1$) — the bounds of the query.
It is guaranteed the sum of $n$ over all test cases does not exceed $2 \cdot 10^5$ and the sum of $q$ over all test cases does not exceed $2 \cdot 10^5$.
Output
Output $\sum_{j=l+k-1}^{r} f([a_l, a_{l+1}, \ldots, a_j])$ for each query on a new line.
Example
Input
3
7 5 3
1 2 3 2 1 2 3
1 7
2 7
3 7
8 4 2
4 3 1 1 2 4 3 2
3 6
1 5
5 4 2
4 5 1 2 3
1 4
1 5
Output
6
5
2
2
5
2
3
Note
In the second query of the first testcase, we calculate the following function values:
- $f([2,3,2,1,2])=3$ because Yunli can set $b_3=4$, $b_4=5$, and $b_5=6$, making a consecutive subarray of size $5$ in $3$ moves.
- $f([2,3,2,1,2,3]=2$ because we can set $b_3=0$ and $b_2=-1$, making a consecutive subarray of size $5$ in $2$ moves (starting at position $2$)
The answer to this query is $3+2=5$.
解题思路
逆天 Div.4,想着补个 G 题的结果一题都不会(),G3 的难度预测甚至到了 2800 分,直接弃疗.jpg。
做 G1 的时候一直从差分的角度去想,结果做不出来,题解给出的性质反正我是观察不出来的。假设子数组 $a_l, a_{l + 1}, \ldots, a_r$ $(r-l+1=k)$ 满足连续性,那么对于任意 $l \leq i \leq j \leq r$,有 $a_j - a_i = j - i$,移项得到 $a_i - i = a_j - j$。定义 $b_i = a_i - i$,因此如果子数组要满足连续性,等价于 $b_l = b_{l + 1} = \ldots = b_r$。假设出现次数最多 $b_i$ 的值为 $x$,我们将 $b_i$ 都变成 $x$,就可以用最少的修改将子数组变得连续。
定义 $f(i) \, (1 \leq i \leq n-k+1)$ 表示将子数组 $a_i, a_{i+1}, \ldots, a_{i+k-1}$ 变得连续的最少修改次数。可以用大小为 $k$ 的滑动窗口预处理出每个 $f(i)$,开个 std::map
维护窗口内 $a_i - i$ 的个数,std::multiset
维窗口内值出现的次数。对于 G1 就可以 $O(1)$ 查询了。
G2 的思路也显而易见了,就是在 $f(l), f(l+1), \ldots, f(r-k+1)$ 中,求每个前缀的最小值的和,即 $\sum\limits_{i = l}^{r-k+1}{\min\limits_{l \leq j \leq i}{f(j)}}$。不过题解给出来的做法我也想不出来就是了。
做法是按询问的左端点从大到小离线处理,下面解释这样做的原因。不妨假设我们当前维护出了 $g(i), g(i+1), \ldots, g(n-k+1)$,其中 $g(j) \, (i \leq j \leq n-k+1)$ 表示前缀最小值即 $\min\limits_{i \leq x \leq j}{f(x)}$。对于询问只需求出关于 $g(j)$ 某个前缀的和(此时的 $i$ 一定是该询问的左端点)。由于询问的左端点依次减小,因此需要从后往前依次把 $f(i)$ 考虑进来去维护每个 $g(j)$。
现在把 $f(i-1)$ 考虑进来,并相应地将 $g(j)$ 都更新成 $\min\limits_{i-1 \leq x \leq j}{f(x)}$,如何维护呢?只需从 $i-1$ 开始往右找到第一个比 $f(i-1)$ 小的 $f(u)$(可以维护一个单调栈),并将 $g(i-1), g(i), \ldots, g(u-1)$ 都更新成 $f(i-1)$ 即可。由于涉及到区间修改,查询区间和,因此可以用线段树去维护 $g(j)$。
AC 代码如下,时间复杂度为 $O(q \log{q} + (q + n) \log{n})$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5;
int a[N], f[N];
int l[N], r[N], p[N];
struct Node {
int l, r, tag;
LL s;
}tr[N * 4];
int stk[N];
LL ans[N];
void build(int u, int l, int r) {
tr[u] = {l, r, -1};
if (l != r) {
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
}
void upd(int u, int c) {
tr[u].s = (tr[u].r - tr[u].l + 1ll) * c;
tr[u].tag = c;
}
void pushdown(int u) {
if (tr[u].tag == -1) return;
upd(u << 1, tr[u].tag);
upd(u << 1 | 1, tr[u].tag);
tr[u].tag = -1;
}
void modify(int u, int l, int r, int 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);
tr[u].s = tr[u << 1].s + tr[u << 1 | 1].s;
}
}
LL query(int u, int l, int r) {
if (tr[u].l >= l && tr[u].r <= r) return tr[u].s;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (r <= mid) return query(u << 1, l, r);
if (l >= mid + 1) return query(u << 1 | 1, l, r);
return query(u << 1, l, r) + query(u << 1 | 1, l, r);
}
void solve() {
int n, m, k;
cin >> n >> k >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
a[i] -= i;
}
map<int, int> mp;
multiset<int> st;
for (int i = 0; i < n; i++) {
st.insert(0);
}
for (int i = 1; i <= n; i++) {
st.erase(st.find(mp[a[i]]));
st.insert(++mp[a[i]]);
if (i - k > 0) {
st.erase(st.find(mp[a[i - k]]));
st.insert(--mp[a[i - k]]);
}
if (i >= k) f[i - k + 1] = k - *st.rbegin();
}
for (int i = 1; i <= m; i++) {
cin >> l[i] >> r[i];
p[i] = i;
}
sort(p + 1, p + m + 1, [&](int i, int j) {
return l[i] > l[j];
});
build(1, 1, n - k + 1);
int tp = 0;
stk[++tp] = n - k + 2;
f[n - k + 2] = -1;
for (int i = 1, j = n - k + 1; i <= m; i++) {
while (j >= l[p[i]]) {
while (f[stk[tp]] > f[j]) {
tp--;
}
modify(1, j, stk[tp] - 1, f[j]);
stk[++tp] = j--;
}
ans[p[i]] = query(1, l[p[i]], r[p[i]] - k + 1);
}
for (int i = 1; i <= m; i++) {
cout << ans[i] << '\n';
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
参考资料
Codeforces Round 971 (Div. 4) Editorial:https://codeforces.com/blog/entry/133296
标签:G2,int,hard,tp,st,leq,version,query,ldots From: https://www.cnblogs.com/onlyblues/p/18399273