BZOJ4240: 有趣的家庭菜园

【传送门:BZOJ4240


简要题意:

  给出一个长度为n的序列,可以将相邻的数交换位置,要求通过最少交换次数使得这个序列呈左边段不递减,右边段不递增


题解:

  树状数组+贪心

  将每个数一开始的下标为原本的位置,最后得到的序列的逆序对数就是操作的次数

  首先得到的序列肯定是最大的在中间,第二大的在旁边。。

  那么就贪心放值,放的时候找左右两边能产生逆序对最少的放就可以了,用树状数组维护逆序对数

  注意一下多个值相等的情况,且要加long long


参考代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
typedef long long LL;
int a[310000];
int lowbit(int x){return x&-x;}
int n;
void change(int x,int d)
{
    while(x<=n)
    {
        a[x]+=d;
        x+=lowbit(x);
    }
}
int getsum(int x)
{
    int ans=0;
    while(x!=0)
    {
        ans+=a[x];
        x-=lowbit(x);
    }
    return ans;
}
struct node
{
    int id,d;
}h[310000];
bool cmp(node n1,node n2)
{
    return n1.d>n2.d;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&h[i].d);
        h[i].id=i;
    }
    sort(h+1,h+n+1,cmp);
    LL ans=0;
    for(int i=1;i<=n;)
    {
        int j=i-1;
        do
        {
            j++;
            int sum=getsum(h[j].id);
            ans+=min(sum,i-1-sum);
        }while(h[j+1].d==h[j].d);
        for(;i<=j;i++) change(h[i].id,1);
    }
    printf("%lld
",ans);
    return 0;
}

 

原文地址:https://www.cnblogs.com/Never-mind/p/8881228.html