给定 T T 组数据,每组数据给出 n,m,k n , m , k ,有 N N 个数 A1,A2,...,An−1,An A 1 , A 2 , . . . , A n − 1 , A n ,按原顺序,将其划分成几个区间,并保证每个区间里,任意取出 M M 对数其差的平方和小于k ,如果取不出 m m 对就尽量多取,问最少能划分成多少个区间。
T≤12 T ≤ 12
1≤n,m≤5×105 1 ≤ n , m ≤ 5 × 10 5
0≤k≤1018 0 ≤ k ≤ 10 18
0≤Pi≤220 0 ≤ P i ≤ 2 20
固定区间左侧,类似倍增的思想枚举右侧,
设当前的左侧是 l l ,然后 [l,l+2r] [ l , l + 2 r ] 是合法的, [l,l+2r+1] [ l , l + 2 r + 1 ] 是不合法的,
那么在我们此时在 [l+2r,l+2r+1] [ l + 2 r , l + 2 r + 1 ] 二分
然后每个区间的合法就是要满足那个小于 k k ,就是就是最大的 m m 个数跟最小的 m m 个数一一对应然后计算得到的结果小于 k k ,因为这样计算最大,而最大值小于 k k 则这个区间合法。
#include
#include
#include
#include
#include
#define N 500005
using namespace std;
typedef long long ll;
int n, m, tot;
ll a[N], b[N];
ll k;
bool cmp(int x, int y)
{
return a[x] < a[y];
}
bool Check(int l, int r)
{
int num = 1;
while(l + num - 1 <= r) b[num] = a[l + num - 1], num++;
num--;
sort(b + 1, b + num + 1);
int x = 1, y = num, cnt = 0;
ll cp = 0;
while (x < y && cnt < m)
{
cp += (b[y] - b[x]) * (b[y] - b[x]);
if (cp > k) return 0;
x++, y--, cnt++;
}
return cp <= k;
}
bool Work(int id)
{
int x = 1, y = tot, cnt = 0;
ll res = 0;
while (x < y && cnt < m)
{
cnt++;
while (x < y && b[x] > id) x++;
while (x < y && b[y] > id) y--;
if (x >= y) break;
res += (a[b[y]] - a[b[x]]) * (a[b[y]] - a[b[x]]);
x++, y--;
if (res > k) return 0;
}
return res <= k;
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
scanf("%d %d %lld", &n, &m, &k);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
int ans = 0, l = 1;
while (l <= n)
{
int num = 1;
while (l + num <= n && Check(l, l + num)) num <<= 1;
int x = l + num / 2, y = l + num;
if (y > n) y = n;
tot = 0;
for (int i = l; i <= y; i++) b[++tot] = i;
sort(b + 1, b + tot + 1, cmp);
int now = x;
while (x <= y)
{
int mid = (x + y) >> 1;
if (Work(mid)) now = mid, x = mid + 1;
else y = mid - 1;
}
l = now + 1;
ans++;
}
printf("%d\n", ans);
}
return 0;
}