题目意思:
给定n, expect, a, b 要求你构造一组array[],存放一个1..n的排列,使的下面的程序能输出YES
题目所示代码:
1 bool less_than(x, y) { 2 T++; 3 return x < y; 4 } 5 void work(array[], l, r) { 6 if (l >= r) return; 7 swap(array[(l * A + r * B) / (A + B)], array[r]); 8 int index = l; 9 for (i = l; i < r; i++) 10 if (less_than(array[i], array[r])) 11 swap(array[index++], array[i]); 12 swap(array[r], array[index]); 13 work(array, l, index - 1); 14 work(array, index + 1, r); 15 } 16 void main() { 17 T = 0; 18 Input(n, expect, A, B, array[]); 19 work(array, 0, n - 1); 20 if (T == expect) 21 Output("YES"); 22 else 23 Output("NO"); 24 }
壕无疑问这是一个快排!
n个数排列,T的上限是sigma(1..n-1) = n*(n-1)/2
而下限则可以通过递推得到
mi[x] = 0 for x =0, 1
mi[x] = x-1 + min(mi[i] + mi[x-i-1]) (0 <=i < x) for x > 1
当然根据经验得到,i应该是取x/2的时候最好,跑了一下,mi数组也确实没变。。当然跑n^2的应该时间也够。
而且实际上 mi[i] + mi[x-i-1] 在i = 0..x/2呈现非递增的性质,解法用到了这个性质。
当expext在上下限之间的时候,可以通过递归构造
void work(int l, int r, int c)
构造区间[l,r] 使得这个区间的T = c
显然这个区间要消耗r - l次
设对这个区间的第一次排序后中间数在m位置,还要消耗区间[l,m - 1], [m+1, r]
当m = l .. (l+r)/2 (Ps: 当 m > (l+r)/2时 和<= (l+r)/2的情况是等价的)
假如[l,m - 1], [m+1, r]均取最小值,则数值呈现非递减
假如[l,m - 1], [m+1, r]均取最大值,则数值呈现非递减
由于这个区间一定存在m符合条件,那么取第一个使得区间消耗最小值mi(l,m - 1) + mi(m+1, r) <= c - (r-l) 的必然符合要求(Ps: 把最小值和最大值在纸上画一下函数就明了)
让左区间取消耗最小值, 然后递归构造
work(l,m-1,mi[l-m]) work(m1,r,c - (r-l) - mi[l-m])
我们希望区间[l, r]第一次sort后,p[l..r] = [ p[l..m-1], m ,p[m+1..r] ]
那么,通过给定的a, b计算得到m原本的下标,手动回滚他程序中的两次swap。
构造完毕,代码如下:
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 using namespace std; 6 const int N = 10005; 7 int a,b,p[N]; 8 int mi[N]; 9 inline bool can(int l,int r,int c){ 10 int n = r-l+1; 11 return c>=mi[n] && c<=n*(n-1)/2; 12 } 13 inline void mysort(int l,int m,int r,int id){ 14 p[m] = m; 15 swap(p[m],p[r]); 16 swap(p[r],p[id]); 17 } 18 void work(int l,int r,int c){ 19 if(l>r) return; 20 if(l == r){ 21 p[l] = l; 22 return; 23 } 24 int lt = l,rt = (l+r)/2+1, m; 25 c -= r-l; 26 while(lt<rt){ 27 m = (lt + rt)>>1; 28 if(mi[m-l]+mi[r-m] > c) lt = m + 1; 29 else rt = m; 30 } 31 m = lt; 32 work(l,m-1,mi[m-l]); 33 work(m+1,r,c-mi[m-l]); 34 mysort(l,m,r,(l*a + r*b)/(a+b)); 35 } 36 int n,m; 37 int main(){ 38 //freopen("in.txt", "r", stdin); 39 for(int i=2;i<N;i++){ 40 int x = i/2, y = i-x-1; 41 mi[i] = mi[x]+mi[y]+i-1; 42 } 43 //for(int i=0;i<=20;i++)cout<<i<<" "<<mi[i]<<endl; 44 while(~scanf("%d%d%d%d",&n,&m,&a,&b)){ 45 if(!can(0,n-1,m)){ 46 puts("NOWAY"); 47 continue; 48 } 49 work(0,n-1,m); 50 for(int i=0;i<n;i++) printf(i==n-1?"%d\n":"%d ",p[i]+1); 51 } 52 return 0; 53 }