题目链接:点击查看
题目大意:给出一个长度为 n 的序列,每次操作可以交换相邻两个数字的位置,现在问最小进行多少次操作,可以使得序列的相对大小呈山峰状(中间高两边低,或非严格递增或非严格递减)
题目分析:简单分析过后不难看出,对于位置 i 的数字来说,最后的位置只会有两种情况,一种是位于上升的山坡上,另一种是位于下降的山坡上,分类讨论一下:
- 如果位置 i 最终位于上升的山坡上,那么至少需要与其左边所有大于他的数完成一次交换
- 如果位置 i 最终位于下降的山坡上,那么至少需要与其右边所有大于他的数完成一次交换
这样我们只需要对每个数统计一下贡献就好了,看一下是将其放在上升的山坡上还是下降的山坡上更优即可
这样问题就转换为了求某个数前面有多少个比他大的数,经典的逆序对问题,用树状数组或者线段树去解决就好了
代码:
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<list>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const LL inf=0x3f3f3f3f3f3f3f3f;
const int N=1e5+100;
int a[N],c[N];
LL pre[N],nt[N];
int lowbit(int x)
{
return x&(-x);
}
void add(int x)
{
for(int i=x;i<N;i+=lowbit(i))
c[i]++;
}
int ask(int x)
{
int ans=0;
for(int i=x;i>0;i-=lowbit(i))
ans+=c[i];
return ans;
}
void init()
{
memset(c,0,sizeof(c));
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
init();
for(int i=1;i<=n;i++)
{
pre[i]=ask(N-1)-ask(a[i]);
add(a[i]);
}
init();
for(int i=n;i>=1;i--)
{
nt[i]=ask(N-1)-ask(a[i]);
add(a[i]);
}
LL ans=0;
for(int i=1;i<=n;i++)
ans+=min(pre[i],nt[i]);
printf("%lld\n",ans);
return 0;
}