Description
给定一个长度为N的数列A以及一个整数T.我们要把A分成若干段,使得每一段的'校验值'都不超过N.求最少需要分成几段.
Sol
首先是校验值的求法:
要使得'每对数的差的平方'之和最大,显然就是先排序,然后取最大和最小为一对,次大和次小为一对.....
然后是问题的转化:求最少分的段数,显然就是确定左端点后,在校验值不超过T的前提下尽量扩展右端点.
优化就在于右端点的扩展,当然就是用倍增辣qwq
还有就是求校验值的优化:可以不用每次都快排,而是先排增加的一段,然后归并就好了
Code
#include<iostream> #include<cstdio> #include<algorithm> #define il inline #define Rg register #define go(i,a,b) for(Rg int i=a;i<=b;i++) #define yes(i,a,b) for(Rg int i=a;i>=b;i++) #define ll long long using namespace std; il int read() { int x=0,y=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();} return x*y; } int T,n,m,as,a[500001],b[500001],c[500001]; ll K; il bool ck(int l,int r,int md) { go(i,md,r)b[i]=a[i]; sort(b+md,b+r+1); int i=l,j=md; go(k,l,r) if((i<=md-1 && b[i]<b[j]) || j>r)c[k]=b[i++]; else c[k]=b[j++]; i=l,j=r;int t=min(m,(r-l+1)/2);ll nw=0; while(t--) nw+=1LL*(c[i]-c[j])*(c[i]-c[j]),i++,j--; if(nw<=K) { go(i,l,r)b[i]=c[i]; return 1; } return 0; } int main() { T=read(); while(T--) { n=read(),m=read();scanf("%lld",&K);as=0; go(i,1,n)a[i]=read(); int l=1,r=1,p=1; b[1]=a[1]; while(l<=n) { if(r+p<=n && ck(l,r+p,r+1))r+=p,p*=2; else p/=2; if(!p || r==n){as++,l=++r,p=1,b[l]=a[l];} } printf("%d ",as); } return 0; }