What Goes Up Must Come Down(变成山峰数组最小步数)

题目大意:

题意:给定 n 个整数的数组,通过移动相邻两个整数,使得这个数组构成一个单峰数组(前面非递减,后面非递增),求最少的移动次数。

题目解析:

分析:小的数一定在两边(要么左边,要么右边),所以可以从小到大判定所有数是移到左边移动次数少还是右边,树状数组维护一下就好了。

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=1e6+100;
int n;
ll c[maxn],a[maxn];
vector<int>v[maxn];
int lowbit(int x){
    return x&-x;
}
void add(int x,ll k){
    for(int i=x;i<=n;i+=lowbit(i)){
        c[i]+=k;
    }
}
ll query(int x){
    ll ans=0;
    for(int i=x;i>0;i-=lowbit(i)){
        ans+=c[i];
    }
    return ans;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        v[a[i]].push_back(i);
        add(i,1); 
    }
    ll ans=0;
    int z=n;
    for(int i=1;i<=1e5;i++){
        if(v[i].size()==0){
            continue;
        }
        for(int j=0;j<v[i].size();j++){
            add(v[i][j],-1);//把这个数的贡献减掉 
            z--;//数的总个数 
        }
        for(int j=0;j<v[i].size();j++){
            ans+=min(z-query(v[i][j]),query(v[i][j]-1));//移动到左边或者右边 
        }
    }
    cout<<ans<<endl;
}

 

posted @ 2021-09-22 21:56  lipu123  阅读(220)  评论(0)    收藏  举报