排序 题解
排序 题解
题意
\(~~~~\) 给出一种排序方式,对于一个待排序区间若存在 \(i\) 使得 \(\min_{j=1}^i a_j \leq \max_{j=i+1}^n a_j\) ,则递归排序 \([1,i]\) 和 \([i+1,n]\) ,否则进行一轮冒泡排序并累积区间长度的复杂度。求整个排序的总复杂度。
本文版权归 Azazel 与博客园共有,欢迎转载,但需保留此声明,并给出原文地址,谢谢合作。
原文地址:https://www.cnblogs.com/Azazel/p/15194554.html
题解
\(~~~~\) 首先手玩一下样例,依稀可以感觉到对于某个 \(a_i\) ,其总共被向后冒泡的次数与之后所有小于它的数有关。更进一步,我们猜测其对复杂度的贡献为 \(j-i+1\) ,其中 \(j\) 是最大的使得 \(a_j<a_i\) 的数。
\(~~~~\) 考虑如何证明其正确性暴力对拍,对于某个 \(i\) ,若其之后存在 \(j\) 使得 \(a_j<a_i\) ,则 \(i\) 跟 \(j\) 不可能被分裂为两个区间递归排序,此时则 \(i\) 跟 \(j\) 定会进行若干轮冒泡排序,直至不再存在这样的 \(j\) 。而之后已经归位 \(i\) 就会作为一个 “间断点” 使得其作为它所在区间的最后一个元素,此时的 \(a_i\) 不可能主动再去交换其他数,故在不存在 \(a_j< a_i\) 时 \(a_i\) 就会停止贡献。
\(~~~~\) 现在考虑某个数会主动向后交换多少次,对于 \(a_i\) ,若其后面为连续的一段长为 \(len\) 的比它小的数,则其会主动向后交换 \(len\) 次,复杂度累积 \(len+1\) ,而原先的 \(a_{i+len+1}\) 也会向后交换,并最终会交换出所有小于 \(a_i\) 的数在 \(i\) 后面,同时 \(a_i\) 也会继续向后累积复杂度,最终就会发现 \(i\) 直到最后一个比它小的 \(j\) 都会累积复杂度。
\(~~~~\) 突然发现有一个可能更清楚的证法:
\(~~~~\) 来举个例子:\(20\ 9\ 8\ 30\ 11\ 40\ 10\ 100\),然后来计算 \(20\) 的复杂度贡献。
\(~~~~\) 第一次冒泡排序后,\(20\) 会把 \(9\ 8\) 都甩到前面,同时 \(30\) 和 \(40\) 也会把它后面的数甩到前面,序列变成这个样子:\(9\ 8\ 20\ 11\ 30\ 10\ 40\ 100\),此时仅 \(20\) 产生的复杂度贡献为区间 \([1,3]\) 的长度(即可以认为是 \(20\) 曾经到过的位置),同时前面的两个数会被划走,之后只用考虑后面一段;
\(~~~~\) 第二次冒泡排序后,序列变成:\(11\ 20\ 10\ 30\ 40\ 100\) ,此时 \(20\) 产生的复杂度贡献为(原)区间 \([3,4]\) 的长度,平移一下可以认为是 \([4,5]\),同时 \(11\) 可以丢掉。
\(~~~~\) 第三次冒泡排序后,序列变成:\(10\ 20\ 30\ 40\ 100\) ,此时 \(20\) 产生的复杂度贡献为(原)区间 \([4,5]\) 的长度,平移一下可以认为是 \([6,7]\) 。
\(~~~~\) 三次的区间合并一下即为区间 \([1,7]\) 的长度,即刚好是 \(20\) 的位置为左端点,最后一个小于 \(20\) 的数的位置作为右端点的区间。总结一下,其实每次冒泡排序产生的复杂度即为之后连续的一串小于自己的数的长度 \(+1\) ( \(+1\) ),而除去第一次,之后每次停下必定是因为有 \(1\) 个大于自身的数阻挡自己,则把这个数也算入贡献,最后就可以得到上面的式子。
\(~~~~\) 在这之后只需要用树状数组找到最大的大于自己的数的下标即可。时间复杂度 \(\mathcal{O(n\log n)}\)。
代码
查看代码
#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
int n,arr[100005],brr[100005];
ll Ans;
struct BIT{
int tr[100005];
inline int lowbit(int x){return x&(-x);}
void Add(int x,int val){for(;x<=n;x+=lowbit(x)) tr[x]=max(val,tr[x]);}
int Query(int x){int ret=0;for(;x;x-=lowbit(x)) ret=max(tr[x],ret);return ret;}
}BIT;
int main() {
// freopen("sort.in","r",stdin);
// freopen("my.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&arr[i]);
brr[i]=arr[i];
}
sort(brr+1,brr+1+n);
int cnt=unique(brr+1,brr+1+n)-brr-1;
for(int i=1;i<=n;i++) arr[i]=lower_bound(brr+1,brr+1+cnt,arr[i])-brr;
for(int i=n;i>=1;i--)
{
int res=BIT.Query(arr[i]-1);
if(!res) res=i-1;Ans+=res-i+1;
BIT.Add(arr[i],i);
}
printf("%lld",Ans);
return 0;
}