没办法,这题长得就是个数据结构好题,想了两三个小时的线段树也不能怪我。
过分的是,中午我还跑去跟lbc说这题是个数据结构好题,就有点搞笑了,不知道他现在怎么看我。。。
施大orz告诉我们,这题应该用倍增来做。
首先写一个nlogn的check(l,r)函数,来判断区间[l,r]是否符合题意。
考虑一个TLE的倍增,就是从一个点l开始,从大到小枚举t,当check(l,l+(1<<t))==1时就往前跳,然后就没有然后了。
前面全是废话,接下来才是重点。
施大说,从一个点开始先从小到大枚举t,枚举到最大的t使check(l,l+(1<<t))==1后往前跳,然后再从大到小枚举t继续跳。复杂度玄学,但想想还是可以快一些的。
这个代码太丑了,用来对拍还可以,千万别拿去抄。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define sqr(i) i*i
#define ll long long
#define pli pair<ll,int>
#define mkp make_pair
#define X first
#define Y second
const int N=500005;
ll n,m,k,a[N],b[N],cs,ans;
bool check(ll l,ll r){
ll i,x,y,sum=0;
rep(i,l,r)b[i]=a[i];
sort(b+l,b+r+1);
rep(i,0,m-1){
x=l+i;y=r-i;
if(x>=y)break;
sum+=(b[x]-b[y])*(b[x]-b[y]);
}
return sum<=k;
}
int main(){
ll i,l,r,t;
scanf("%lld%lld%lld",&n,&m,&k);
rep(i,1,n)scanf("%lld",&a[i]);
for(cs=0;(1<<cs)<n;++cs);
for(l=1;l<=n;++ans,l=r+1){
rep(t,1,cs){
r=l+(1<<t)-1;
if(!check(l,r))
break;
}
r=l+(1<<t-1)-1;
for(t-=2;t>=0;--t){
if(check(l,r+(1<<t)))r+=1<<t;
}
}
printf("%lld\n",ans);
return 0;
}