蓝书(算法竞赛进阶指南)刷题记录——CH0601 Genius ACM

版权声明:转载请注明原出处啦(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/82703099

题目:0601 Genius ACM.

题目大意:给定一串序列,要你把序列分成几段,使得每段的SPD值都小于T,求最小段数.其中一段序列的SPD值是指,在这段序列中取出M对数(若不足M堆则尽量多取),使得这M对数每对数(a,b)的(a-b)^2的和的最大值.

一段序列的SPD值怎么求?我们可以贪心地让最大差最大,次大差次大...也就是说让这段序列选出的M对数为(最大值,最小值),(次大值,次小值)...

然后我们考虑暴力去做这道题,那么我们就可以枚举.容易发现肯定是取得越多越好,所以我们就枚举一边.每次都判断当前段SPD值是否小于T,判断的时候每次排序,也可以每次都在原来排好的序列中插入数字,时间复杂度O(n^2logn)O(n^2).

我们发现,SPD值计算一次最少也要O(n),所以我们不可能从SPD值的计算当中着手优化.于是我们考虑如何优化枚举.

显然,这道题求的值具有单调性,我们或许可以二分,但是若设最后的段数位ans,那么时间复杂度就是O(ans*nlogn).

我们发现ans一大,二分就不行了.

我们考虑换成倍增,每次呈两倍增长要加长的长度,可以得到一个长度len,那么接下来要增长的长度不超过len,我们就可以再二分增长,得到一个最长长度len,这样我们就可以得出答案了.时间复杂度O(nlog^2n).

我们发现这个算法明显跑不满,但是出题人把这个算法卡掉了.

于是我们发现,上一次排好序的序列实际上这一次是可以直接用的,不需要重新排序.那么就每一次把增加的长度排序,然后与原来的序列合并.

关于时间复杂度,一段区间[l,r]的倍增时间复杂度为O(\sum_{i=1}^{log(r-l+1)}2^i*log(2^i))=O((r-l+1)log(r-l+1)),那么也就是说倍增部分的时间复杂度最多是O(nlogn).同理二分也是O(nlog(n)),总的加起来就是O(nlogn).

那么代码实现如下:

#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;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/82703099