天才ACM
给定一个整数 M,对于任意一个整数集合 S,定义“校验值”如下:
从集合 S 中取出 M 对数(即 2∗M个数,不能重复使用集合中的数,如果 S 中的整数不够 M 对,则取到不能取为止),使得“每对数的差的平方”之和最大,这个最大值就称为集合 SS 的“校验值”。
现在给定一个长度为 N 的数列 A 以及一个整数 T。
我们要把 A 分成若干段,使得每一段的“校验值”都不超过 T。
求最少需要分成几段。
输入格式
第一行输入整数 K,代表有 K 组测试数据。
对于每组测试数据,第一行包含三个整数 N,M,T 。
第二行包含 N 个整数,表示数列A1,A2…AN.
输出格式
对于每组测试数据,输出其答案,每个答案占一行。
数据范围
1
≤
K
≤
12
1\leq K\leq 12
1≤K≤12
1
≤
N
,
M
≤
500000
1\leq N,M\leq 500000
1≤N,M≤500000
0
≤
T
≤
1
0
18
0\leq T\leq 10^{18}
0≤T≤1018
0
≤
A
i
≤
2
20
0\leq A_i\leq 2^{20}
0≤Ai≤220
输入样例:
2
5 1 49
8 2 1 7 9
5 1 64
8 2 1 7 9
输出样例:
2
1
链接:
https://www.acwing.com/problem/content/description/111/
分析:
首先对于一个集合要使得每对数的差的平方之和最大,那么需要取
(
最
大
值
−
最
小
值
)
2
+
(
次
大
值
−
次
小
值
)
2
+
.
.
.
共
M
对
(最大值-最小值)^ {2} + (次大值-次小值)^2 + ...共M对
(最大值−最小值)2+(次大值−次小值)2+...共M对
这样求的校验值最大。而让分成的段数尽量少,也就是让每一段尽量长,看最终能分几段。
于是,问题转化为了:确定了左端点,让你找到满足情况下(校验值<T)的最大的右端点。
求校验值我们需要排序配对,时间复杂度为O(N*logN)。当校验值上限T比较小时,二分就比较浪费时间。我们可以使用倍增+归并的思想来做这道题。
倍增:
1、初始化len = 1, L=R
2、求出[L,R+len]这一段的校验值,若校验值
≤
T
\leq T
≤T,则R+=len, len *=2,否则len/=2;
3、重复上一步,直到len的值变为0,此时R即为所求
上面这个过程最多循环O(log(N))次,每次循环对长为O(R-L)的一段进行排序,我们可以不对整个区间进行排序,可以采用归并排序的方法,只对新增的后一半排序,前一半之前已对其排好序了,合并两段,复杂度会降到O(Nlog(N))。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 500000 +10;
int n, m;
ll t;
int p[maxn];
int a[maxn];
int b[maxn];
int ans = 0;
void merge(int l, int r, int mid)
{
int pos = l;
int ll = l;
int rr = mid;
while(ll < mid && rr <= r)
{
if(a[ll] < a[rr]){
b[pos++] = a[ll++];
}
else {
b[pos++] = a[rr++];
}
}
while(ll < mid) {
b[pos++] = a[ll++];
}
while(rr <= r) {
b[pos++] = a[rr++];
}
}
bool check(int l, int r, int mid)
{
for(int i = mid; i <= r; i++) {
a[i] = p[i];
}
sort(a+mid, a+r+1);
merge(l, r, mid);
ll sum = 0;
for(int i = 1; i <= (r-l+1)>>1 && i <= m; i++) {
sum += ((ll)(b[r-i+1] - b[l+i-1]) * (ll)((b[r-i+1] - b[l+i-1])));
}
if(sum <= t) {
for(int i = l; i <= r; i++)
a[i] = b[i];
return true;
} else
return false;
}
void solve()
{
int l, r;
l = r = 1;
int len = 1;
a[l] = p[l];
while(r <= n) {
if(!len) {
ans++;
l = (++r);
len = 1;
a[l] = p[l];
}
else if(r + len <= n && check(l, r+len, r+1)){
r = r + len;
len <<= 1;
if(r == n) break;
}
else {
len >>= 1;
}
}
if(r == n)
ans++;
}
int main()
{
ios::sync_with_stdio(false);
int k;
cin >> k;
while(k--) {
cin >> n >> m >> t;
for(int i = 1; i <= n; i++) {
cin >> p[i];
}
ans = 0;
solve();
cout << ans << endl;
}
return 0;
}