传送门
题意
给定一个长度为(n)的整数序列(a),以及一个数(t),和一个(m),将(a)分成子序列,子序列中满足从中取出(m)对数(不能重复取,如果不够取到不能取为止),使得每一对数的差的平方和最大,
将这个最大值定义为序列的校验值,使得子序列的校验值小于等于(t),问最少能够分成多少个子序列
数据范围
(egin{array}{l}1 leq K leq 12 \ 1 leq N, M leq 500000 \ 0 leq T leq 10^{18} \ 0 leq A_{i} leq 2^{20}end{array})
题解
对于一段序列来说,取最大的和最小的组成一对,这样计算出来的和是最大的
-
初始化(p=1,r = l)
-
求出([ l , r+p ])的校验值
- 如果校验值(<=t, r=r + p,p=p imes 2),否则(p= frac{p}{2})
-
重复上步,直到(p=0)
-
以上的过程最多进行(O(logn))次,每次循环都对$ r-l$ 排序,总体为O((nlog^{2}n)),每次求校验值的时候不需要快速排序,用类似归并排序的算法只需要对新增部分排序
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,a,n) for(int i=n-1;i>=a;i--)
const int N=5e5+10;
int _;
int n,m;
ll t;
int a[N],b[N],c[N];
int last;
void merge(int l,int mid,int r){
int i = l,j = mid+1;
for(int k=l;k<=r;k++){
if(j > r || (i <= mid && b[i] < b[j]))
c[k] = b[i++];
else c[k] = b[j++];
}
}
long long cal(int l,int r){
if(r > n) r = n;
int cp = min(m,(r-l+1)/2);
for(int i=last+1,i<=r;i++) b[i] = a[i];
sort(b+last+1,b+r+1);
merge(l,last,r);
ll res=0;
rep(i,0,cp)
res += 1ll * (c[r-i]-c[l+i])*1ll*(c[r-i]-c[l+i]);
return res;
}
void solve()
{
cin>>n>>m>>t;
for(int i=1;i<=n;i++) cin>>a[i];
last=1;
int ans=0,l=1,r=1;
b[1]=a[1];
while(l<=n){
int p=1;
while(p){
long long res = cal(l,r+p);
if(res <= t){
last = r = min( r+p , n );
for(int i=l;i<=r;i++) b[i]=c[i]; // 保存有序形式
if(r==n) break;
p*=2;
}
else p/=2;
}
ans++;
l=r+1;
}
cout<<ans<<endl;
}
int main(){
cin>>_;
while(_--)
solve();
}