题意:给一段序列,每次只能移动相邻两位,问最少要移动多少次,才能使整段序列先不单调递减,再不单调递增(允许整段序列单调)
思路:
对于这个序列,是不是要把除了最大以外的数想最大项的左右移动,那么我们按照从小到大的顺序移动,对于当前数为我们有两个方向去移动它,第一种是向左移动,第二种是向右移动,所以我们要求出这个数左右两个方向的逆序数,比较我们应该向哪边移动步数最小,当然是选逆序数少的一端,那为什么移动的步数是逆序数呢?因为我们是按照从小到大的顺序进行移动,那么把当前要移动的数,它左右两端左右某一个到要放置的之前所有数子都比它大,因为按照从小到大的顺序进行移动,所以前面移动不影响当前的数的左右逆序数反而还把这个数的两端全部移动成了大于当前要移动数的数字,所以我们只需要每一次都加上每个数左右两端较少的一个逆序数就可以了
这个操作用树状数组进行维护,当然也可以用线段树,但是没有必要,树状数组的复杂度要小于线段树
ac代码
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <string>
#include <stack>
#include <queue>
#include <map>
#include <vector>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int MAXN = 5e5 + 10;
const int Mod = 1e9 + 7;
const int MAX = 1e5;
int disleft[MAXN] , disright[MAXN];
int arr[MAXN];
int c[MAXN];
int n;
int lowbit(int x)
{
return x & (-x);
}
void Update(ll x, ll d)
{
for(x; x <= MAX; x += lowbit(x)) c[x] += d;
}
ll Query(ll x)
{
ll res = 0;
for(x; x > 0; x -= lowbit(x)) res += c[x];
return res;
}
void Solve()
{
for(int i = 1; i <= n; i++){
disleft[i] = Query(MAX) - Query(arr[i]);
Update(arr[i] , 1);
}
memset(c,0,sizeof c);
for(int i = n; i > 0; i--){
disright[i] = Query(MAX) - Query(arr[i]);
Update(arr[i] , 1);
}
//for(int i = 1; i <= n; i++) cout<<disleft[i]<<' ';
ll ans = 0;
for(int i = 1; i <= n; i++) ans += min(disright[i] , disleft[i]);
cout<<ans<<endl;
}
int main()
{
std::ios::sync_with_stdio(false);
cin>>n;
for(int i = 1; i <= n; i++) cin>>arr[i];
Solve();
}