试题编号:BZOJ4240
输入描述
第一行一个正整数N,代表田地被分为了N个区域。
输出描述
输出一行一个整数,表示最少需要的操作次数
输入样例
6
2
8
4
5
3
6
输出样例
3
提示
试题分析
①题意
原来的题目太恶心了。给出一个序列,只能相邻交换。最终要求每个数左边或右边没有比它大的。求最小移动次数
②算法
冒着被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