基本思路:从最左段开始,每次将右端点尽量大地往右放。

伪代码类似这样:

l = 1
cnt = 0
while l <= n
    l = jump(l) + 1 // jump(l) 即 r
    cnt = 1
print cnt

另外还需要 check 函数检验 是否符合题意

现在考虑 函数的实现

jump 函数 实现方法 1

枚举法,伪代码类似这样:

function jump(l) // ver 1
    r = l
    while check(l, r) == true && r <= n
        r++;
    return r

check 函数 实现方法 1

暴力法,伪代码类似这样:

function check(l, r) // ver 1
    sort(l, r)
    cnt = 1
    tot = 0;
    while cnt <= m && l <= r
        tot += pow(p[l] - p[r], 2)
    return tot <= k

复杂度:

使用 jump(ver 1) 与 check(ver 1)

复杂度:
炸完,没试过,但肯定 TLE (除非

jump 函数 实现方法 2

考虑二分寻找 ,伪代码类似这样:

function jump(l) // ver 2
    L = l, R = n, r = l
    while L <= R
        mid = (L + r) / 2
        if check(l, mid) L = mid + 1, r = mid
        else R = mid - 1
    // 不同人二分实现方式可能略有不同
    return r

使用 jump(ver 2) 与 check(ver 1)

复杂度:
卡的很紧,亲测 45pts,也许 松排 可以过

jump 函数 实现方法 3

考虑倍增寻找 ,伪代码类似这样:

function jump(l) // ver 3
    p = 1
    r = l
    while p != 0 && r <= n
        if check(l, r + p) r += p, p *= 2;
        else p /= 2
    return r

使用了类似二进制优化的思想,当 较小的时候略去了大量无用的判断,有优化作用。

使用 jump(ver 3) 与 check(ver 1)

复杂度:也是 但实际效果比 jump(ver 2) 好得多
data#8 比较大,亲测 91pts,松排 肯定可以过,但完全没必要

check 函数 实现方法 2

配合 jump(ver 3) 食用

考虑每次 check(l, r + p) 的时候, 区间事实上是已经排好序的,可以加以利用。
考虑仅对 排序,然后 类似归并排序地合并

伪代码类似这样

function check(l, r, newR) // ver 2
    sort(r + 1, newR)
    merge([l, r], [r + 1, newR])
    cnt = 1
    tot = 0;
    while cnt <= m && l <= r
        tot += pow(p[l] - p[r], 2)
    return tot <= k

注意:如果 check 返回 false,应当还原 至上一状态(上次 check 后,本次 check 的归并前)否则 的数据就不能被下一次所利用。

使用 jump(ver 3) 与 check(ver 2)

复杂度:也是
data#8 毫无压力,亲测 977ms,不到时限的一半。

代码

已略去头文件 & I/O优化

//     自己选择的路,跪着也要走完。朋友们,虽然这个世界日益浮躁起来,只
// 要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也
// 能够保持自己的本色走下去。                               ——陈立杰
/*************************************
 * @problem:      3758: 0601 Genius ACM.
 * @user_id:      1677.
 * @user_name:    zhaoyi20(brealid).
 * @time:         2020-06-01.
 * @language:     C++.
 * @upload_place: XOJ.
*************************************/ 

const int N = 500007;
int T, n, m; int64 k;
int p[N], tmp[N], tmpMerge[N], save[N];

void merge(int p1, int e1, int p2, int e2) {
    int p = p1, it = p1;
    while (p1 <= e1 && p2 <= e2) {
        if (tmp[p1] < tmp[p2]) tmpMerge[p++] = tmp[p1++];
        else tmpMerge[p++] = tmp[p2++];
    }
    while (p1 <= e1) {
        tmpMerge[p++] = tmp[p1++];
    }
    while (p2 <= e2) {
        tmpMerge[p++] = tmp[p2++];
    }
    while (it <= e2) {
        tmp[it] = tmpMerge[it];
        it++;
    }
}

bool check(int l, int mid, int r) {
    r = min(r, n);
    for (int i = mid + 1; i <= r; i++) tmp[i] = p[i];
    sort(tmp + mid + 1, tmp + r + 1);
    merge(l, mid, mid + 1, r);
    int64 ans(0);
    for (int i = 1, p = l, q = r; i <= m && p < q; i++, p++, q--) {
        ans += (int64)(tmp[p] - tmp[q]) * (tmp[p] - tmp[q]);
        if (ans > k) return false;
    }
    return true;
}

signed main() {
    T = read<int>();
    while (T--) {
        n = read<int>();
        m = read<int>();
        k = read<int64>();
        for (int i = 1; i <= n; i++) p[i] = read<int>();
        int cnt = 0;
        for (int l = 1, r, kth; l <= n; l = r + 1) {
            kth = 1; r = l;
            tmp[l] = p[l];
            while (kth && r <= n) {
                for (int i = l; i <= r; i++) save[i] = tmp[i];
                if (check(l, r, r + kth)) r += kth, kth <<= 1;
                else {
                    kth >>= 1;
                    for (int i = l; i <= r; i++) tmp[i] = save[i];
                }
            }
            cnt++;
        }
        write(cnt, 10);
    }
    return 0;
}

// Create File Date : 2020-06-01