jzoj 3462. 【NOIP2013模拟联考5】休息(逆序对)

3462. 【NOIP2013模拟联考5】休息

Description

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

Input

第一行一个正整数n, 为书的总数。

接下来一行n个数,第i个正整数Hi,为第i 本书的高度。

Output

仅一个整数,为LMZ 需要做的翻转操作的次数。

Sample Input

6

5 3 2 1 6 4

Sample Output

3

【样例解释】

第一次划分之后,翻转(5,3,2,1),(6,4)。之后,书的高度为1 2 3 5 4 6,然后便是翻转(5,4)即可。

Data Constraint

对于10%的数据:n<=50

对于40%的数据:n<=3000

对于100%的数据:1<=n<=100000, 1<=Hi<=n。

分析:100%算法:一个并不明显的性质是,在对原序列进行第一次划分过后,以后
的每次划分得到的各个部分都恰好由两个数组成。 这是因为在第一次划分和
第一轮的翻转之后,原数列由若干个单调增序列拼接而成,形式如下:a1, a2…
ai, b1, b2…bj…z1…zk.考虑相邻两个部分,不妨设为 a 部分和 b 部分,其中 ai
和 b1前后相邻。若 ai

#include 
#include 
#define ll long long
#define N 200000
using namespace std;

struct arr
{
    ll val,num;
}a[N];
ll c[N],n,ans;

int cmp(arr x,arr y) {return x.val>y.val;}

void add(int x)
{
    while (x<=n)
    {
        c[x]++;
        x+=x&(-x);
    }
}

int sum(int x)
{
    int s=0;
    while (x>0)
    {
        s+=c[x];
        x-=x&(-x);
    }
    return s;
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i].val);
    int i=1;
    while (iint j=i;
        while (a[i].val>a[i+1].val&&iint k=i;
        bool fl=false;
        while (jtrue;
            swap(a[j].val,a[k].val);
            j++;k--;
        }
        if (fl) ans++;
        i++;    
    }
    for (int i=1;i<=n;i++)
        a[i].num=i;
    sort(a+1,a+n+1,cmp);
    for (int i=1;i<=n;i++)
    {
        ans+=sum(a[i].num);
        add(a[i].num);
    }
    printf("%lld",ans);
}

你可能感兴趣的:(树状数组,离散)