题目链接
http://noi-test.zzstep.com/contest/0x00%E3%80%8C%E5%9F%BA%E6%9C%AC%E7%AE%97%E6%B3%95%E3%80%8D%E4%BE%8B%E9%A2%98/0601%20Genius%20ACM
分析
每次设定一段长度为 1 1 1 的初始区间(一定合法),倍增来扩展区间右端点。
求“校验值”需要排序,只需排新加入的一段,再像归并排序那样合并两段。
注意每次扩展还要保证新的右端点不超过 N N N。
AC代码
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
inline ll read() {
ll num = 0;
char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9')
num = num * 10 + c - '0', c = getchar();
return num;
}
const int maxn = 5e5 + 5;
int a[maxn], tmp[maxn];
int main() {
int t = read();
while (t--) {
int n = read(), m = read(), cnt = 0;
ll k = read();
for (int i = 1; i <= n; ++i) a[i] = read();
int l = 1, r = 1, p = 1;
while (l <= n) {
while (r + p > n) p /= 2;
for (int i = r + 1; i <= r + p; ++i) tmp[i] = a[i];
sort(tmp + r + 1, tmp + r + p + 1);
int lp = l, rp = r + 1;
for (int i = l; i <= r + p; ++i) {
if (rp > r + p || (lp <= r && a[lp] <= tmp[rp]))
tmp[i] = a[lp++];
else tmp[i] = tmp[rp++];
}
ll val = 0;
for (int i = l; i <= l + min(m, (r + p - l + 1) / 2) - 1; ++i)
val += pow(tmp[i] - tmp[r + p - i + l], 2);
if (val <= k) {
for (int i = l; i <= r + p; ++i) a[i] = tmp[i];
r = r + p, p *= 2;
} else p /= 2;
if (!p) ++cnt, l = r + 1, r = l, p = 1;
}
printf("%d\n", cnt);
}
return 0;
}