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