【OI做题记录】【BZOJ】有趣的家庭菜园

试题编号:BZOJ4240

对家庭菜园有兴趣的JOI君每年在自家的田地中种植一种叫做IOI草的植物。JOI君的田地沿东西方向被划分为N个区域,由西到东标号为1~N。IOI草一共有N株,每个区域种植着一株。在第i个区域种植的IOI草,在春天的时候高度会生长至hi,此后便不再生长。
为了观察春天的样子而出行的JOI君注意到了IOI草的配置与预定的不太一样。IOI草是一种非常依靠阳光的植物,如果某个区域的IOI草的东侧和西侧都有比它高的IOI草存在,那么这株IOI草就会在夏天之前枯萎。换句话说,为了不让任何一株IOI草枯萎,需要满足以下条件:
对于任意2<=i<=N-1,以下两个条件至少满足一个:
1. 对于任意1<=j<=i-1,hj<=hi
2. 对于任意i+1<=j<=N,hk<=hi
IOI草是非常昂贵的,为了不让IOI草枯萎,JOI君需要调换IOI草的顺序。IOI草非常非常的高大且纤细的植物,因此JOI君每次只能交换相邻两株IOI草。也就是说,JOI君每次需要选择一个整数i(1<=i<=N-1),然后交换第i株IOI草和第i+1株IOI草。随着夏天临近,IOI草枯萎的可能性越来越大,因此JOI君想知道让所有IOI草都不会枯萎的最少操作次数。
现在给出田地的区域数,以及每株IOI草的高度,请你求出让所有IOI草的不会枯萎的最少操作次数。

输入描述

第一行一个正整数N,代表田地被分为了N个区域。
接下来N行,第i行(1<=i<=N)一个整数hi,表示第i株植物在春天时的高度

输出描述

输出一行一个整数,表示最少需要的操作次数

输入样例

6
2
8
4
5
3
6

输出样例

3

提示

最终的高度序列为2 4 5 8 6 3,共需要操作三次。
3<=N<=3*10^5
1<=hi<=10^9

试题分析

①题意

原来的题目太恶心了。给出一个序列,只能相邻交换。最终要求每个数左边右边没有比它大的。求最小移动次数

②算法

冒着被scy打死的风险做新的题目。首先我们不难发现,我不断移动较小的数,较大的数是不会被影响的。换种说法,无论我怎么移动较小的,较大的原来满足条件照样满足条件,原来不满足条件照样不满足条件。

以这个为突破口,我们先将整个序列从大到小排序,然后重新放。想象一下,先放最大的,很明显符合条件。接下来放第二大的。它可以放在最大的左边、或者最大的右边。第三大,只能放在整个序列的最左边或者最右边。以此类推。

这个问题就变成了一个求逆序对的问题了。在原序列中,将左侧大于i的数的数目记为Li,右侧大于i的数的数目记为Ri,那么如果将一个点放在新序列的左侧,贡献的逆序对就是Li,反之为Ri。

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


至于怎么求Li,Ri,用树状数组求。从大到小插入点的原位置(add(a[i].id)),统计的时候Li=getsum(a[i].id)

需要注意的是,如果两个数大小相同,则后面的数不增加逆序对。

#include
#include
#include
#include
#include
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;
}
long long mymin(long long x,long long y)
{
    return x


你可能感兴趣的:(清华四百步,OI,树状数组)