基本思路:从最左段开始,每次将右端点尽量大地往右放。
伪代码类似这样:
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