题意:给定 n 个整数的数组,通过移动相邻两个整数,使得这个数组构成一个单峰数组(前面非递减,后面非递增),求最少的移动次数。
分析:小的数一定在两边(要么左边,要么右边),所以可以从小到大判定所有数是移到左边移动次数少还是右边,树状数组维护一下就好了。
代码:
#include<vector>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N= 1E5+10;
int c[N];
int n;
int lowbit(int x){return x&(-x);}
void update(int index,int x)
{
while(index<=n)
{
c[index]+=x;
index+=lowbit(index);
}
}
ll sum(int x)
{
ll ans=0;
while(x>0)
{
ans+=c[x];
x-=lowbit(x);
}
return ans;
}
vector<int>a[N];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
a[x].push_back(i);
update(i,1);
}
ll res=n;
ll ans=0;
for(int i=1;i<=1E5;i++)
{
if(a[i].empty()) continue;
for(int j=0;j<a[i].size();j++)
{
update(a[i][j],-1);
res--;
}
for(int j=0;j<a[i].size();j++)
{
ans+=min(res-sum(a[i][j]),sum(a[i][j]-1));
}
}
printf("%lld",ans);
return 0;
}