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 rl+k1 for all queries.

For an arbitrary array b, Yunli can perform the following operation any number of times:

  • Select an index i. Set bi=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 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 j=l+k1rf([al,al+1,,aj]).

If there exists a consecutive subarray of length k that starts at index i (1i|b|k+1), then bj=bj1+1 for all i<ji+k1.

Input

The first line contains t (1t104) — the number of test cases.

The first line of each test case contains three integers n, k, and q (1kn2105, 1q2105) — the length of the array, the length of the consecutive subarray, and the number of queries.

The following line contains n integers a1,a2,...,an (1ain).

The following q lines contain two integers l and r (1lrn, rl+k1) — the bounds of the query.

It is guaranteed the sum of n over all test cases does not exceed 2105 and the sum of q over all test cases does not exceed 2105.

Output

Output j=l+k1rf([al,al+1,,aj]) 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 b3=4, b4=5, and b5=6, making a consecutive subarray of size 5 in 3 moves.
  • f([2,3,2,1,2,3]=2 because we can set b3=0 and b2=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 的时候一直从差分的角度去想,结果做不出来,题解给出的性质反正我是观察不出来的。假设子数组 al,al+1,,ar (rl+1=k) 满足连续性,那么对于任意 lijr,有 ajai=ji,移项得到 aii=ajj。定义 bi=aii,因此如果子数组要满足连续性,等价于 bl=bl+1==br。假设出现次数最多 bi 的值为 x,我们将 bi 都变成 x,就可以用最少的修改将子数组变得连续。

  定义 f(i)(1ink+1) 表示将子数组 ai,ai+1,,ai+k1 变得连续的最少修改次数。可以用大小为 k 的滑动窗口预处理出每个 f(i),开个 std::map 维护窗口内 aii 的个数,std::multiset 维窗口内值出现的次数。对于 G1 就可以 O(1) 查询了。

  G2 的思路也显而易见了,就是在 f(l),f(l+1),,f(rk+1) 中,求每个前缀的最小值的和,即 i=lrk+1minljif(j)。不过题解给出来的做法我也想不出来就是了。

  做法是按询问的左端点从大到小离线处理,下面解释这样做的原因。不妨假设我们当前维护出了 g(i),g(i+1),,g(nk+1),其中 g(j)(ijnk+1) 表示前缀最小值即 minixjf(x)。对于询问只需求出关于 g(j) 某个前缀的和(此时的 i 一定是该询问的左端点)。由于询问的左端点依次减小,因此需要从后往前依次把 f(i) 考虑进来去维护每个 g(j)

  现在把 f(i1) 考虑进来,并相应地将 g(j) 都更新成 mini1xjf(x),如何维护呢?只需从 i1 开始往右找到第一个比 f(i1) 小的 f(u)(可以维护一个单调栈),并将 g(i1),g(i),,g(u1) 都更新成 f(i1) 即可。由于涉及到区间修改,查询区间和,因此可以用线段树去维护 g(j)

  AC 代码如下,时间复杂度为 O(qlogq+(q+n)logn)

#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

posted @   onlyblues  阅读(204)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的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
Web Analytics
点击右上角即可分享
微信分享提示