[jzoj 3462] 【NOIP2013模拟联考5】休息 {归并排序(逆序对)+快速排序}

题目

Description
休息的时候,可以放松放松浑身的肌肉,打扫打扫卫生,感觉很舒服。在某一天,某LMZ 开始整理他那书架。已知他的书有n 本,从左到右按顺序排列。他想把书从矮到高排好序,而每一本书都有一个独一无二的高度Hi。他排序的方法是:每一次将所有的书划分为尽量少的连续部分,使得每一部分的书的高度都是单调下降,然后将其中所有不少于2 本书的区间全部翻转。重复执行以上操作,最后使得书的高度全部单调上升。可是毕竟是休息时间,LMZ 不想花太多时间在给书排序这种事上面。因此他划分并翻转完第一次书之后,他想计算,他一共执行了多少次翻转操作才能把所有的书排好序。LMZ 惊奇地发现,第一次排序之前,他第一次划分出来的所有区间的长度都是偶数。
Input
第一行一个正整数n, 为书的总数。
接下来一行n个数,第i个正整数Hi,为第i 本书的高度。
Output
仅一个整数,为LMZ 需要做的翻转操作的次数。


解题思路

10%算法:呵呵,对于这种神奇的算法还是表示不明觉厉。
100%算法:一个并不明显的性质是,在对原序列进行第一次划分过后,以后
的每次划分得到的各个部分都恰好由两个数组成。 这是因为在第一次划分和
第一轮的翻转之后,原数列由若干个单调增序列拼接而成,形式如下: a1,a2ai,b1,b2bjz1zk. a 1 , a 2 … a i , b 1 , b 2 … b j … z 1 … z k . 考虑相邻两个部分,不妨设为 a 部分和 b 部分,其中 ai和 b1前后相邻。若 ai<b1 a i < b 1 则可以合并成同一部分,否则会形成块 (ai,b1) ( a i , b 1 ) ,并且在下一轮翻转,成为 ai1,b1,ai,b2 … a i − 1 , b 1 , a i , b 2 … 。可以发现在这以后,由于有 ai1<ai a i − 1 < a i ,则 ai1 a i − 1 ai a i 不会在同一个块里;同理 b1 b 1 b2 b 2 不会在同一个块里。即,任意连续三个数必定不会是单调递减的。 那么,这以后每次翻转都只会将相邻的逆序对交换。由于这个算法最终可以正确地得到结果,所以第一轮以后进行的翻转操作数就等于第一轮之后序列的逆序对数。而第一轮的翻转数我们可以直接模拟得到。 求一个序列的逆序对数的经典做法是归并排序,同时记录。时间复杂度 O(nlog2n) O ( n l o g 2 n )


代码

#include
#include
using namespace std;
int t[400005],n,a[400005],tot,b[400005]; long long ans; 

void merge(int l,int r){
    if(l==r) return;
    int mid=(l+r)>>1;
    merge(l,mid);  merge(mid+1,r);
    int i=l,j=mid+1,p=l;
    while(i<=mid&&j<=r)
    {
        if(a[i]>a[j])
        {
            t[p++]=a[j++];
            ans+=mid-i+1;
        }
        else t[p++]=a[i++];
    }
    while(i<=mid)  t[p++]=a[i++];
    while(j<=r)   t[p++]=a[j++];
    for(i=l;i<=r;i++)
      a[i]=t[i];
}

void kp(int l,int r)
{
    if (l>=r) return;
    int i=l,j=r,mid=a[(l+r)/2];
    do
    {
        while (a[i]while (a[j]>mid) j--;
        if (i<=j)
        {
            swap(a[i],a[j]);
            i++; j--;
        }
    }while (i<=j);
}

int main(){
    scanf("%d",&n);
    scanf("%d",&a[1]); b[++tot]=1; 
    for(int i=2;i<=n;i++) 
    {
        scanf("%d",&a[i]);
        if (a[i]>a[i-1]) b[++tot]=i; 
    } 
    b[++tot]=n; 
    for (int i=2;i<=tot;i++)
     kp(b[i-1],b[i]),ans++; 
    merge(1,n);
    printf("%lld",ans); 
}

你可能感兴趣的:(数据结构(/堆排序/桶/数))