给定一个整数 MM,对于任意一个整数集合 SS,定义“校验值”如下:
从集合 SS 中取出 MM 对数(即 2×M2×M 个数,不能重复使用集合中的数,如果 SS 中的整数不够 MM 对,则取到不能取为止),使得“每对数的差的平方”之和最大,这个最大值就称为集合 SS 的“校验值”。
现在给定一个长度为 NN 的数列 AA 以及一个整数 TT。
我们要把 AA 分成若干段,使得每一段的“校验值”都不超过 TT。
求最少需要分成几段。
输入格式
第一行输入整数 KK,代表有 KK 组测试数据。
对于每组测试数据,第一行包含三个整数 N,M,TN,M,T 。
第二行包含 NN 个整数,表示数列A1,A2…ANA1,A2…AN。
输出格式
对于每组测试数据,输出其答案,每个答案占一行。
数据范围
1≤K≤121≤K≤12,
1≤N,M≤5000001≤N,M≤500000,
0≤T≤10180≤T≤1018,
0≤Ai≤2200≤Ai≤220
输入样例:
2
5 1 49
8 2 1 7 9
5 1 64
8 2 1 7 9
输出样例:
2
1
解题思路:
/*
大概思路:
使用倍增的方法先求出区间[L, L + len]的校验值
若成立则令L += len , len <<= 1
若不成立则令len >>= 1直到len为0循环结束
每对数的差的平方”之和最大因此要使它最大则应该是
最小的数与最大的数配对,次小的数与次大的数配对
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 500010;
LL T;
int n, m;
int a[N], temp[N], t[N];
//a[N]为A数组,temp为归并排序中的临时数组, t[N]为当前正在倍增的数组
LL sq(int x)
{
return (LL)x * x;
}
bool check(int l, int mid, int r)
{
for (int i = mid; i < r; i ++ ) t[i] = a[i];//因为[l, mid)在上一轮以及被排序计算过,所以直接从mid开始
sort(t + mid, t + r);//将t的区间排序
int i = l, j = mid, k = 0;
while (i < mid && j < r)//将排好序的t放入temp中,此时temp里面的数都是从小到大排序的
{
if (t[i] < t[j]) temp[k ++ ] = t[i ++ ];
else temp[k ++ ] = t[j ++ ];
}
while (i < mid) temp[k ++ ] = t[i ++ ];
while (j < r) temp[k ++ ] = t[j ++ ];
LL sum = 0;
for (int i = 0; i < m && i < k; i ++, k -- )//检查校验值是否符合要求,题目说了校验值是
sum += sq(temp[i] - temp[k - 1]); //每对数的差的平方”之和最大因此要使它最大则应该是
//最小的数与最大的数配对,次小的数与次大的数配对
return sum <= T; //因为temp从小到大排序因此选择第一个与最后一个配对
}
int main()
{
int cnt;
cin >> cnt;
while (cnt -- )
{
scanf("%d %d %lld", &n, &m, &T);
for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
int ans = 0, len;
int start = 0, last = 0;
while (last < n)
{
len = 1;
while (len)
{
if (last + len <= n && check(start, last, last + len))//判断[start, last + len)是否符合要求
{
last += len, len <<= 1;//若成立利用倍增思想扩大范围
if (last >= n) break;//若last >= n,则last + len比大于n + 1题解结束
for (int i = start; i < last; i ++ )//经过check后数组temp中的[start, len]以及被排序过
t[i] = temp[i - start];//将他放入倍增数组t以免重复排序
}
else len >>= 1;//若不成立缩小范围
}
start = last;//当len == 0说明该区间[start, last]已选得最大区间长度的符合要求的校验值
ans ++ ;//所以开始下一个区间的选取
}
printf("%d\n", ans);
}
}
版权声明:本文为qq_61935738原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。