[关闭]
@ysner 2018-09-22T09:00:16.000000Z 字数 2591 阅读 1782

[noi.ac_D1T2]sort

脑洞


题面

定义"翻转排序":每次操作均为把区间中所有数倒过来,即
每次操作的代价为
给一个序列,用"翻转排序"给它排序,并把代价控制在以内。

解析

感觉很符合难度。。。

算法

仔细想想,"翻转"这个条件挺恶心的。
可以不"翻转"吗?那就只能交换相邻两个。

把序列扫遍,每次只交换序列中相邻两个数。
这样每交换一次对应着消除一个逆序对,复杂度可控。
次数最多为
但是我并不知道这种排序名字叫什么。。。

复杂度
代码在下一档。

算法

这个有点搞笑。
直接归并排序就可以了。
每次归并后把左边的区间和右边的区间并在一起,翻转即可。
复杂度
贴上代码。

  1. #include<iostream>
  2. #include<cstring>
  3. #include<cmath>
  4. #include<cstdio>
  5. #include<cstdlib>
  6. #include<algorithm>
  7. #define ll long long
  8. #define re register
  9. #define il inline
  10. #define pc(a) putchar(a)
  11. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  12. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  13. using namespace std;
  14. const int N=1e5+100;
  15. int n,a[N];
  16. il ll gi()
  17. {
  18. re ll x=0,t=1;
  19. re char ch=getchar();
  20. while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  21. if(ch=='-') t=-1,ch=getchar();
  22. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  23. return x*t;
  24. }
  25. il void wri(re int x)
  26. {
  27. if(x>9) wri(x/10);
  28. pc(x%10+48);
  29. }
  30. il void solve(re int l,re int r)
  31. {
  32. if(l==r) return;
  33. if(l==r-1)
  34. {
  35. if(a[l]>a[r]) swap(a[l],a[r]),printf("%d %d\n",l,r);
  36. return;
  37. }
  38. re int mid=l+r>>1,p1=0,p2=0;
  39. solve(l,mid);solve(mid+1,r);
  40. fp(i,l,mid) if(a[i]==1) {p1=i;break;}
  41. fp(i,mid+1,r) if(a[i]==1) {p2=i;break;}
  42. if(!p1) return;
  43. if(!p2) {printf("%d %d\n",p1,r);reverse(a+p1,a+r+1);return;}
  44. printf("%d %d\n",p1,p2-1);reverse(a+p1,a+p2);
  45. return;
  46. }
  47. int main()
  48. {
  49. n=gi();
  50. fp(i,1,n) a[i]=gi();
  51. if(n<=5000)
  52. {
  53. fp(i,1,n)
  54. {
  55. re int tag=0;
  56. fp(j,1,n-1)
  57. if(a[j]>a[j+1]) tag=1,wri(j),pc(' '),wri(j+1),pc('\n'),swap(a[j],a[j+1]);
  58. if(!tag) break;
  59. }
  60. puts("-1 -1");
  61. return 0;
  62. }
  63. solve(1,n);
  64. puts("-1 -1");
  65. return 0;
  66. }

算法

蒟蒻其实并不知道快排原理
快排的原理是,在向下分治前,先选取一个基准数,通过归并排序,把该分治区间中的小于等于其的数移到左边,大于其的数移到右边。
归并的过程中可以通过"翻转",把左区间中的大于其的数与右区间中小于等于其的数一次交换完毕。
然后继续向下分治即可。
基准数就是选,该区间中间位置,在排序完后的中对应的值。(记得把值离散化)
复杂度
很对但是想不到。。。

  1. #include<iostream>
  2. #include<cmath>
  3. #include<cstdio>
  4. #include<cstdlib>
  5. #include<cstring>
  6. #include<algorithm>
  7. #define ll long long
  8. #define re register
  9. #define il inline
  10. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  11. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  12. using namespace std;
  13. const int N=1e5+100;
  14. ll n,a[N],b[N];
  15. il ll gi()
  16. {
  17. re ll x=0,t=1;
  18. re char ch=getchar();
  19. while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  20. if(ch=='-') t=-1,ch=getchar();
  21. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  22. return x*t;
  23. }
  24. il int Qsort(re int l,re int r,re ll B)
  25. {
  26. if(l==r) return l+(a[l]<=B);
  27. re int mid=l+r>>1,p1=Qsort(l,mid,B),p2=Qsort(mid+1,r,B)-1;
  28. if(p1!=mid+1&&p2!=mid)
  29. {
  30. printf("%d %d\n",p1,p2);
  31. reverse(a+p1,a+p2+1);
  32. }
  33. return p1+(p2-mid);
  34. }
  35. il void solve(re int l,re int r)
  36. {
  37. if(l==r) return;
  38. re int mid=l+r>>1;
  39. Qsort(l,r,b[mid]);
  40. solve(l,mid);solve(mid+1,r);
  41. }
  42. int main()
  43. {
  44. n=gi();
  45. fp(i,1,n) b[i]=a[i]=gi()*n+i;
  46. sort(b+1,b+1+n);
  47. solve(1,n);
  48. puts("-1 -1");
  49. return 0;
  50. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注