[笔记]ACM笔记 - 排序小技巧

it2022-07-04  111

Description

一个数组,要求先对前n个数字排序(以方便后续操作);又要求对前n+i个数字排序;又要求对前n+j … 前n+k个数字排序(i、j、k的大小远小于n,且i、j、k间没有大小关系)。总之就是对一个不定的范围内数据要进行频繁的按大小顺序调用,但是这个范围边界变化不大,很多数据重叠,这样每次都对此次区间内数据排序,频繁排序的话很费时间。

例如一个数组{1,3,6,5,2,4,1,9,0},一共9个数字,下标0~8。要求: 每次取一个区间,计算区间内()2+()2+()2+...的值。很容易想到对区间排个序,即可方便获得最大、次大值等。 对1~5排序:{2,3,4,5,6} 对1~6排序:{1,2,3,4,5,6} 对2~5排序:{2,4,5,6} 可以看出2、5、6始终在范围内,但每次都要针对所选区间重新排序,很麻烦。 既然大部分数据一直出现在范围内,现在就希望能够一次排序,应对所有情况。

Key

继续使用上述的例子:Array={1,3,6,5,2,4,1,9,0} 开个新数组作其索引:Index={0,1,2,3,4,5,6,7,8} 令索引数组按照Array的大小关系排序,得Index={8,0,6,4,1,5,3,2,7}

对于区间[1, 5]:从左向右找出第一个在[1, 5]的下标即为最小值:8不符合、0不符合、6不符合,4符合,那么最小值就是Array[4]=2,次大值就是Array[1]=3

即每次只需检测排序后当前位的数字的下标是否在该区间内即可。

Sample

题目:https://hihocoder.com/problemset/problem/1384 一道贪心的题,期间需要对下标i到j、i到j+k之间的数字分别排序。是别人家的代码(他的原文链接,虽然我也不知道他是不是转别人的),就是在这学到的技巧。注意观察judge函数与judge2函数的差异,judge2函数即实现了上述排序思想。

#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; typedef long long int ll; ll m,n,k; ll a[500050]; ll b[500050]; int cnt=0; inline bool cmp(int x,int y) { return a[x]<a[y]; } bool judge(int l,int r) { int pos=0; while(l+pos<=r)b[pos]=a[l+pos],pos++; sort(b,b+pos); pos--; int mid=(pos-1)/2; ll res=0; for(int i=0; i<=mid&&i<m; i++) { res+=(b[i]-b[pos-i])*(b[i]-b[pos-i]); if(res>k) return false; } return res<=k; } void init(int l,int r) { cnt=0; for(int i=l; i<=r; i++)b[cnt++]=i; sort(b,b+cnt,cmp); } bool judge2(int r) { int i,j,kk; ll res=0; for(i=0,j=cnt-1,kk=m; kk; i++,j--,kk--) { while(i<j&&b[i]>r)i++; while(i<j&&b[j]>r)j--; if(i>=j)break; res+=(a[b[i]]-a[b[j]])*(a[b[i]]-a[b[j]]); if(res>k)break; } return res<=k; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%lld%lld%lld",&n,&m,&k); for(int i=0; i<n; i++) scanf("%lld",&a[i]); int ans=0; int l=0; while(l<n) { int kk=1; while(kk+l<n&&judge(l,l+kk)) kk*=2; int first=l+kk/2,last=l+kk-1<n?l+kk-1:n-1; init(l,last); int mid; int pos=l; while(first<=last) { if(judge2(mid=(first+last)/2)) { first=mid+1; pos=mid; } else last=mid-1; } l=pos+1; ans++; } cout<<ans<<endl; } return 0; }

转载于:https://www.cnblogs.com/xienaoban/p/6798056.html


最新回复(0)