Fork me on GitHub

たのしい家庭菜園

たのしい家庭菜園

前置知识:对于一个序列,只能相邻两两交换时,排成有序序列的最小次数为逆序对的个数。

而交换较小的两个数时,对较大的数没有影响,就是满足条件的还是满足条件,不满足的还是不满足

先从大到小排序,得到序列的相对位置(原下标),然后依次插入i 

题目要求最后的序列先不递减再不递增,所以中间那个一定放最高的,然后往两边依次放。

那么现在只需要思考怎么让新序列的逆序对数最少

当放入一个数的时候,判断放左边和右边哪个产生的逆序对数少,贪心的选即可。在原序列中,将左侧大于i的数的数

目记为Li,右侧大于i的数的数目记为Ri,

那么如果将一个点放在新序列的左侧,贡献的逆序对就是Li,反之为Ri。

根据性质:相邻交换次数等于逆序对数,我们对于每个数选取min{Li,Ri}加上去就可以了

 

#include <bits/stdc++.h>
using namespace std;
const int N=300005;
struct qq{
    int x,id;
}a[N];
long long s[N];
long long ans=0;
int n;
int cmp(qq x,qq y) {
    return x.x>y.x;
}
long long lowbit(long long x) {
    return x&(-x);
}
void add(long long x)
{
    while(x<=n)
    {
    	s[x]++;
    	x+=lowbit(x);
    }
}
long long getsum(long long x)
{
    long long sum=0;
    while(x>0)
    {
        sum+=s[x];
        x-=lowbit(x);
    }
    return sum;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i].x);
        a[i].id=i;
    }
    memset(s,0,sizeof(s));
    sort(a+1,a+n+1,cmp);
    int st=1;
    for(int i=1;i<=n;i++) {
        if(a[i].x!=a[i-1].x) {
            while(st<i)
            	add(a[st++].id);
        }
        int soy=getsum(a[i].id);
        ans+=min(soy,st-1-soy);
    }
    printf("%lld",ans);
    return 0;
}

 

posted @ 2022-08-07 11:05  Doria_tt  阅读(43)  评论(0)    收藏  举报