Genius ACM

题解:

发现匹配一定会选最大和最小匹配,确定左右端点之后nlogn排序后算

比较容易想到二分 最坏情况每次1个 $n^2*(logn)^2$

没错暴力的最差复杂度是$n^2*logn$的

发现长度与次数相关

二分改成倍增 $n(logn)^2$

然后大概常数好就过了吧

倍增的时候我们可以把排序看成一段有序的+一段无序的

可以把无序的先排序再和有序的归并

时间复杂度nlogn

代码:

#include <bits/stdc++.h> 
using namespace std;
#define rint register int
#define IL inline
#define rep(i,h,t) for (rint i=h;i<=t;i++)
#define dep(i,t,h) for (rint i=t;i>=h;i--)
#define ll long long
char ss[1<<24],*A=ss,*B=ss;
IL char gc()
{
    return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++; 
}
template<class T>void read(T &x)
{
    rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=(c^48); 
    while (c=gc(),c>47&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f;     
}
const int N=6e5;
int a[N],b[N],n,m,c[N],d[N],e[N],len;
ll k;
IL bool check(rint x,rint y)
{
    rint len=y-x+1;
    rep(i,x,y) b[i-x+1]=a[i];
    sort(b+1,b+len+1);
    rint tmp=len/2;
    ll ans=0;
    rep(i,1,m)
    {
        if (i>tmp) break;
        ans+=1ll*(b[i]-b[len-i+1])*(b[i]-b[len-i+1]);
    }
    if (ans<=k) return(1); else return(0);
}
IL bool check(rint x,rint y,rint h2,rint t2)
{
    rint len2=t2-h2+1;
    rep(i,h2,t2) c[i-h2+1]=a[i];
    sort(c+1,c+len2+1);
    int h1=1,t1=len; h2=1,t2=len2;
    int cnt=0;
    while (h2<=t2&&h1<=t1)
    {
        if (b[h1]<c[h2]) d[++cnt]=b[h1],h1++;
        else d[++cnt]=c[h2],h2++;
    }
    while (h1<=t1) d[++cnt]=b[h1],h1++;
    while (h2<=t2) d[++cnt]=c[h2],h2++;
    rep(i,1,len) e[i]=b[i];
    rep(i,1,cnt) b[i]=d[i];
    rint tmp2=len; len=cnt;
    rint tmp=len/2;
    ll ans=0;
    rep(i,1,m)
    {
        if (i>tmp) break;
        ans+=1ll*(b[i]-b[len-i+1])*(b[i]-b[len-i+1]);
    }
    if (ans<=k) return(1); else
    {
      len=tmp2; 
      rep(i,1,len) b[i]=e[i];
      return(0);
    }
}
IL int js(register int x)
{
    register int y=x;
    register int i=1;
    register int lst=x-1;
    while (i>=0)
    {
      for (i=0;i<=19;i++)
      { 
        if (!check(y,x+(1<<i)-1,lst+1,x+(1<<i)-1)) break;
        lst=x+(1<<i)-1;
      }
      i--;
      x=x+(1<<i)-1;
      i--;
      if (x>n) break;
    }
    return x;
}
int main()
{
    freopen("geniusacm.in","r",stdin);
    freopen("geniusacm.out","w",stdout);
    int T;
    read(T);
    rep(tt,1,T)
    {
        read(n); read(m); read(k);
        rep(i,1,n) read(a[i]);
        int now=1,cnt=0;
        while (now<=n)
        {
            len=0;
            now=js(now)+1;
            cnt++;
        }
        cout<<cnt<<endl;
    }
    return 0; 
} 

 

转载于:https://www.cnblogs.com/yinwuxiao/p/9865790.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值