题目给了个快排程序,要求构造序列使比较次数等于except。
于是给定n,我们可以求出可以到达的最小比较次数,和最大比较次数,n*(n-1)/2
根据快排,每次划分,左边是比其小的数,右边是大的。因此如果划分序列,只需让ans[m]=m;即可划分为m-l-1和r-mid两个,比赛的时候就这里没想清楚。
而最小值和(Min[x]+Min[len-x])最大值和都是单调递减的,二分即可
最后再交换两次,回到k位置即可
其实整个过程就是模拟下快排的逆过程
/* author:jxy lang:C/C++ university:China,Xidian University **If you need to reprint,please indicate the source** */ #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> using namespace std; int Low[10005]; int A,B; int ans[10005]; int Mi(int n)//确定下界 { if(n<=1)return 0; if(~Low[n])return Low[n]; return Low[n]=Mi(n/2)+Mi(n-1-n/2)+n-1; } void solve(int l,int r,int e) { if(l>r)return; if(l==r) { ans[l]=l; return; } int len=r-l; int ll=0,rr=(len+1)>>1,mid; e-=len; while(ll<rr)//二分查找划分长度 { mid=(ll+rr)>>1; if(Mi(mid)+Mi(len-mid)>e)ll=mid+1; else rr=mid; } mid=ll+l; solve(l,mid-1,Mi(ll)); solve(mid+1,r,e-Mi(ll)); ans[mid]=mid; swap(ans[mid],ans[r]); swap(ans[r],ans[(A*l+B*r)/(A+B)]);//返回标兵 } int main() { int n,e; memset(Low,-1,sizeof(Low)); while(~scanf("%d%d%d%d",&n,&e,&A,&B)) { if(Mi(n)>e||n*(n-1)/2<e) puts("NOWAY"); else { solve(1,n,e); for(int i=1;i<=n;i++) { if(i>1)putchar(' '); printf("%d",ans[i]); } puts(""); } } }
#include <cstdio> #include<iostream> #include <algorithm> using namespace std; int T,A,B; bool less_than(int x, int y) { T++; return x < y; } void work(int array[], int l, int r) { if (l >= r) return; swap(array[(l * A + r * B) / (A + B)], array[r]); int index = l; for (int i = l; i < r; i++) if (less_than(array[i], array[r])) swap(array[index++], array[i]); swap(array[r], array[index]); work(array, l, index - 1); work(array, index + 1, r); } int array[100000]; int main() { int n,expect; while(~scanf("%d%d%d%d",&n,&expect,&A,&B)) { T=0; for(int i=0;i<n;i++) scanf("%d",&array[i]); work(array, 0, n - 1); if (T == expect) puts("YES"); else puts("NO"); } }