Yunli’s Subarray Queries (hard version)
#莫队 #数据结构 #线段树 #单调栈
题目描述
This is the hard version of the problem. In this version, it is guaranteed that r ≥ l + k − 1 r \geq l+k-1 r≥l+k−1 for all queries.
For an arbitrary array b b b, Yunli can perform the following operation any number of times:
- Select an index i i i. Set b i = x b_i = x bi=x where x x x is any integer she desires ( x x x is not limited to the interval [ 1 , n ] [1,n] [1,n]).
Denote f ( b ) f(b) f(b) as the minimum number of operations she needs to perform until there exists a consecutive subarray ∗ ^{\text{∗}} ∗ of length at least k k k in b b b.
Yunli is given an array a a a of size n n n and asks you q q q queries. In each query, you must output ∑ j = l + k − 1 r f ( [ a l , a l + 1 , … , a j ] ) \sum_{j=l+k-1}^{r} f([a_l, a_{l+1}, \ldots, a_j]) ∑j=l+k−1rf([al,al+1,…,aj]).
∗ ^{\text{∗}} ∗If there exists a consecutive subarray of length k k k that starts at index i i i ( 1 ≤ i ≤ ∣ b ∣ − k + 1 1 \leq i \leq |b|-k+1 1≤i≤∣b∣−k+1), then b j = b j − 1 + 1 b_j = b_{j-1} + 1 bj=bj−1+1 for all i ≤ j ≤ i + k − 1 i \leq j \leq i+k-1 i≤j≤i+k−1.
输入格式
The first line contains t t t ( 1 ≤ t ≤ 1 0 4 1 \leq t \leq 10^4 1≤t≤104) — the number of test cases. The first line of each test case contains three integers n n n, k k k, and q q q ( 1 ≤ k ≤ n ≤ 2 ⋅ 1 0 5 1 \leq k \leq n \leq 2 \cdot 10^5 1≤k≤n≤2⋅105, 1 ≤ q ≤ 2 ⋅ 1 0 5 1 \leq q \leq 2 \cdot 10^5 1≤q≤2⋅105) — the length of the array, the length of the consecutive subarray, and the number of queries. The following line contains n n n integers a 1 , a 2 , . . . , a n a_1, a_2, ..., a_n a1,a2,...,an ( 1 ≤ a i ≤ n 1 \leq a_i \leq n 1≤ai≤n). The following q q q lines contain two integers l l l and r r r ( 1 ≤ l ≤ r ≤ n 1 \leq l \leq r \leq n 1≤l≤r≤n, r ≥ l + k − 1 r \geq l+k-1 r≥l+k−1) — the bounds of the query. It is guaranteed the sum of n n n over all test cases does not exceed 2 ⋅ 1 0 5 2 \cdot 10^5 2⋅105 and the sum of q q q over all test cases does not exceed 2 ⋅ 1 0 5 2 \cdot 10^5 2⋅105.
输出格式
Output ∑ j = l + k − 1 r f ( [ a l , a l + 1 , … , a j ] ) \sum_{j=l+k-1}^{r} f([a_l, a_{l+1}, \ldots, a_j]) ∑j=l+k−1rf([al,al+1,…,aj]) for each query on a new line.
样例 #1
样例输入 #1
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
样例输出 #1
6
5
2
2
5
2
3
解法
解题思路
延续之前 e a s y easy easy版本的做法,我们可以预处理出所有 k k k大小窗口的 f ( 1 ) , f ( 2 ) . . . f ( n − k + 1 ) f(1),f(2)...f(n-k + 1) f(1),f(2)...f(n−k+1),这里使用莫队来预处理。
容易发现, f ( 2 ) f(2) f(2)可以取 m i n ( f ( 2 ) , f ( 3 ) ) min(f(2),f(3)) min(f(2),f(3)), f ( 1 ) f(1) f(1)可以取 m i n ( f ( 1 ) , f ( 2 ) , f ( 2 ) ) min(f(1),f(2),f(2)) min(f(1),f(2),f(2))…,因此我们处理后的 f f f实际上是低调不增的。
在 h a r d hard hard版本中,我们需要求的 f ( l ) , f ( l + 1 ) , f ( l + 2 ) . . . f ( r − k + 1 ) f(l),f(l+1),f(l+2)...f(r-k+1) f(l),f(l+1),f(l+2)...f(r−k+1)这些的和,这一部分显然是可以通过线段树来求和的。
同样我们采用离线的方式,把询问存起来,这里按照左端点从大到小来排序。
考虑枚举左端点 i i i,然后通过 f ( i ) f(i) f(i)来更新, f ( i + 1 ) f(i+1) f(i+1)包含的区间,因为更新的区间一定是一段单调不增的区间。这部分可以使用单调栈来找到第一个小于等于 f ( i ) f(i) f(i)的值,或者直接二分那个位置即可。
而更新操作就使用线段树推平赋值即可,最后判断每个查询 [ l , r ] [l,r] [l,r]是否在区间内,通过线段树区间求和来更新答案。
代码
const int N = 2e5 + 10;
int n, m, k, len;
struct Mos {
int l, r, id;
bool operator<(Mos& x) {
if ((l - 1) / len + 1 != (x.l - 1) / len + 1) return l < x.l;
if (((l - 1) / len + 1) & 1) return r < x.r;
else return r > x.r;
}
};
struct node {
int l, r;
ll sum, flg;
}tr[N * 4];
void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void build(int u, int l, int r) {
if (l == r) tr[u] = { l, r, 0, -1 };
else {
tr[u] = { l, r, 0, -1 };
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void pushdown(int u) {
node& root = tr[u], & ls = tr[u << 1], & rs = tr[u << 1 | 1];
if (root.flg != -1) {
ls.sum = (ls.r - ls.l + 1ll) * root.flg;
ls.flg = root.flg;
rs.sum = (rs.r - rs.l + 1ll) * root.flg;
rs.flg = root.flg;
root.flg = -1;
}
}
void modify(int u, int l, int r, int c) {
if (tr[u].l >= l && tr[u].r <= r) {
tr[u].sum = (tr[u].r - tr[u].l + 1ll) * c;
tr[u].flg = 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) modify(u << 1 | 1, l, r, c);
pushup(u);
}
}
ll query(int u, int l, int r) {
if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
else {
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
ll res = 0;
if (l <= mid) res += query(u << 1, l, r);
if (r > mid) res += query(u << 1 | 1, l, r);
return res;
}
}
int stk[N];
int a[N],f[N];
void solve() {
std::cin >> n >> k >> m;
len = 350;
for (int i = 1; i <= n; ++i) {
std::cin >> a[i];
}
std::vector<Mos>q(n + 1);
for (int i = 1; i <=n-k+1 ; ++i) {
int l = i, r = l + k - 1;
q[i] = { l,r,i };
}
std::vector<int>mp(2 * n + 1);
std::multiset<int>mst;
sort(q.begin() + 1, q.begin() + n - k + 2);
auto add = [&](int idx) {
mst.extract(mp[a[idx] - idx + n]);
mp[a[idx] - idx + n]++;
mst.insert(mp[a[idx] - idx + n]);
};
auto del = [&](int idx) {
mst.extract(mp[a[idx] - idx + n]);
mp[a[idx] - idx + n]--;
mst.insert(mp[a[idx] - idx + n]);
};
int l = 1, r = 0;
for (int i = 1; i <= n-k+1; ++i) {
while (l > q[i].l) add(--l);
while (l < q[i].l) del(l++);
while (r < q[i].r) add(++r);
while (r > q[i].r) del(r--);
int len = r - l + 1;
f[q[i].id] = len - *mst.rbegin();
}
std::vector<std::array<int, 3>>Q;
for (int i = 1; i <= m; ++i) {
int l, r;
std::cin >> l >> r;
Q.push_back({ l,r, i });
}
sort(Q.begin(), Q.end(),
[&](const std::array<int, 3>& x, const std::array<int, 3>& y) {
return x[0] > y[0];
});
int p = 0;
int tt = 0;
int ed = n - k + 1;
build(1, 1, ed);
std::vector<int>ans(m + 1);
for (int i = ed; i >= 1; i--) {
while (tt && f[stk[tt - 1]] >= f[i]) tt--;
if (tt) modify(1, i, stk[tt - 1] - 1, f[i]);
else modify(1, i, ed, f[i]);
while (p < Q.size() && Q[p][0] == i) {
ans[Q[p][2]] = query(1, Q[p][0], Q[p][1] - k + 1);
p++;
}
stk[tt++] = i;
}
for (int i = 1; i <= m; i++) {
std::cout << ans[i] << "\n";
}
}
signed main() {
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);
int t = 1;
std::cin >> t;
while (t--) {
solve();
}
}