G2. Yunli's Subarray Queries (hard version)
G2. Yunli's Subarray Queries (hard version)
This is the hard version of the problem. In this version, it is guaranteed that for all queries.
For an arbitrary array , Yunli can perform the following operation any number of times:
- Select an index . Set where is any integer she desires ( is not limited to the interval ).
Denote as the minimum number of operations she needs to perform until there exists a consecutive subarray of length at least in .
Yunli is given an array of size and asks you queries. In each query, you must output .
If there exists a consecutive subarray of length that starts at index (), then for all .
Input
The first line contains () — the number of test cases.
The first line of each test case contains three integers , , and (, ) — the length of the array, the length of the consecutive subarray, and the number of queries.
The following line contains integers ().
The following lines contain two integers and (, ) — the bounds of the query.
It is guaranteed the sum of over all test cases does not exceed and the sum of over all test cases does not exceed .
Output
Output 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:
- because Yunli can set , , and , making a consecutive subarray of size in moves.
- because we can set and , making a consecutive subarray of size in moves (starting at position )
The answer to this query is .
解题思路
逆天 Div.4,想着补个 G 题的结果一题都不会(),G3 的难度预测甚至到了 2800 分,直接弃疗.jpg。
做 G1 的时候一直从差分的角度去想,结果做不出来,题解给出的性质反正我是观察不出来的。假设子数组 满足连续性,那么对于任意 ,有 ,移项得到 。定义 ,因此如果子数组要满足连续性,等价于 。假设出现次数最多 的值为 ,我们将 都变成 ,就可以用最少的修改将子数组变得连续。
定义 表示将子数组 变得连续的最少修改次数。可以用大小为 的滑动窗口预处理出每个 ,开个 std::map
维护窗口内 的个数,std::multiset
维窗口内值出现的次数。对于 G1 就可以 查询了。
G2 的思路也显而易见了,就是在 中,求每个前缀的最小值的和,即 。不过题解给出来的做法我也想不出来就是了。
做法是按询问的左端点从大到小离线处理,下面解释这样做的原因。不妨假设我们当前维护出了 ,其中 表示前缀最小值即 。对于询问只需求出关于 某个前缀的和(此时的 一定是该询问的左端点)。由于询问的左端点依次减小,因此需要从后往前依次把 考虑进来去维护每个 。
现在把 考虑进来,并相应地将 都更新成 ,如何维护呢?只需从 开始往右找到第一个比 小的 (可以维护一个单调栈),并将 都更新成 即可。由于涉及到区间修改,查询区间和,因此可以用线段树去维护 。
AC 代码如下,时间复杂度为 :
#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
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18399273
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2023-09-05 D. Balanced String
2022-09-05 Meeting Rooms III
2022-09-05 Number of Ways to Reach a Position After Exactly k Steps