⭐例题 Genius_ACM

考虑到二分会有很多低效的操作(如:二分第一步检验 0x06 倍增 - 图1 这么长一段但右端点只会移动很少的部分),直接换倍增写。

  1. 初始化 0x06 倍增 - 图2
  2. 求出 0x06 倍增 - 图3 区间的“检验值” ,若 检验值 0x06 倍增 - 图40x06 倍增 - 图5 ,否则 0x06 倍增 - 图6
  3. 重复上一步直到 0x06 倍增 - 图7 ,此时 R 为所求值
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. using ll = long long;
  4. const int N = 500006;
  5. int n, m, w;
  6. ll k, a[N], b[N], c[N];
  7. void gb(int l, int mid, int r) {
  8. int i = l, j = mid + 1;
  9. for (int k = l; k <= r; k++)
  10. if (j > r || (i <= mid && b[i] <= b[j])) c[k] = b[i++];
  11. else
  12. c[k] = b[j++];
  13. }
  14. ll f(int l, int r) {
  15. if (r > n) r = n;
  16. int t = min(m, (r - l + 1) >> 1);
  17. for (int i = w + 1; i <= r; i++) b[i] = a[i];
  18. sort(b + w + 1, b + r + 1);
  19. gb(l, w, r);
  20. ll ans = 0;
  21. for (int i = 0; i < t; i++)
  22. ans += (c[r - i] - c[l + i]) * (c[r - i] - c[l + i]);
  23. return ans;
  24. }
  25. void Genius_ACM() {
  26. cin >> n >> m;
  27. cin >> k;
  28. for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
  29. int ans = 0, l = 1, r = 1;
  30. w = 1;
  31. b[1] = a[1];
  32. while (l <= n) {
  33. int p = 1;
  34. while (p) {
  35. ll num = f(l, r + p);
  36. if (num <= k) {
  37. w = r = min(r + p, n);
  38. for (int i = l; i <= r; i++) b[i] = c[i];
  39. if (r == n) break;
  40. p <<= 1;
  41. } else
  42. p >>= 1;
  43. }
  44. ans++;
  45. l = r + 1;
  46. }
  47. cout << ans << endl;
  48. }
  49. int main() {
  50. int t;
  51. cin >> t;
  52. while (t--) Genius_ACM();
  53. return 0;
  54. }