版权声明:转载请注明原出处啦(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/82703099
题目:0601 Genius ACM.
题目大意:给定一串序列,要你把序列分成几段,使得每段的SPD值都小于T,求最小段数.其中一段序列的SPD值是指,在这段序列中取出M对数(若不足M堆则尽量多取),使得这M对数每对数(a,b)的的和的最大值.
一段序列的SPD值怎么求?我们可以贪心地让最大差最大,次大差次大...也就是说让这段序列选出的M对数为(最大值,最小值),(次大值,次小值)...
然后我们考虑暴力去做这道题,那么我们就可以枚举.容易发现肯定是取得越多越好,所以我们就枚举一边.每次都判断当前段SPD值是否小于T,判断的时候每次排序,也可以每次都在原来排好的序列中插入数字,时间复杂度或.
我们发现,SPD值计算一次最少也要O(n),所以我们不可能从SPD值的计算当中着手优化.于是我们考虑如何优化枚举.
显然,这道题求的值具有单调性,我们或许可以二分,但是若设最后的段数位ans,那么时间复杂度就是.
我们发现ans一大,二分就不行了.
我们考虑换成倍增,每次呈两倍增长要加长的长度,可以得到一个长度len,那么接下来要增长的长度不超过len,我们就可以再二分增长,得到一个最长长度len,这样我们就可以得出答案了.时间复杂度.
我们发现这个算法明显跑不满,但是出题人把这个算法卡掉了.
于是我们发现,上一次排好序的序列实际上这一次是可以直接用的,不需要重新排序.那么就每一次把增加的长度排序,然后与原来的序列合并.
关于时间复杂度,一段区间[l,r]的倍增时间复杂度为,那么也就是说倍增部分的时间复杂度最多是.同理二分也是,总的加起来就是.
扫描二维码关注公众号,回复:
3454838 查看本文章
那么代码实现如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=500000;
int n,m,p[N+9],ans;
int l,r;
LL tmp[N+9],order[N+9];
LL K;
LL sqr(LL a){
return a*a;
}
void merge(int L,int mid,int R){
int i=L,j=mid,k=L;
while (i<mid&&j<=R)
if (order[i]<=order[j]) tmp[k++]=order[i++];
else tmp[k++]=order[j++];
while (i<mid) tmp[k++]=order[i++];
while (j<=R) tmp[k++]=order[j++];
}
bool check(int L,int mid,int R){
for (int i=mid;i<=R;i++)
order[i]=p[i];
stable_sort(order+mid,order+R+1);
merge(L,mid,R);
LL sum=0;
for (int i=1;i<=R-L+1>>1&&i<=m;i++)
sum+=sqr(tmp[R-i+1]-tmp[L+i-1]);
if (sum<=K){
for (int i=L;i<=R;i++)
order[i]=tmp[i];
return true;
}else return false;
}
Abigail into(){
l=r=0;ans=0;
scanf("%d%d%lld",&n,&m,&K);
for (int i=1;i<=n;i++)
scanf("%lld",&p[i]);
}
Abigail work(){
int len=1;
l=r=1;
order[l]=p[l];
while (r<=n)
if (!len){
len=1;ans++;
l=++r;
order[l]=p[l];
}else if (r+len<=n&&check(l,r+1,r+len)) {
r+=len;len<<=1;
if (r==n) break;
}else len>>=1;
if (r==n) ans++;
}
Abigail outo(){
printf("%d\n",ans);
}
int main(){
int T=1;
scanf("%d",&T);
while (T--){
into();
work();
outo();
}
return 0;
}