题目给了个快排程序,要求构造序列使比较次数等于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");
}
}