牛客 - What Goes Up Must Come Down(树状数组求逆序对)

题目链接:点击查看

题目大意:给出一个长度为 n 的序列,每次操作可以交换相邻两个数字的位置,现在问最小进行多少次操作,可以使得序列的相对大小呈山峰状(中间高两边低,或非严格递增或非严格递减)

题目分析:简单分析过后不难看出,对于位置 i 的数字来说,最后的位置只会有两种情况,一种是位于上升的山坡上,另一种是位于下降的山坡上,分类讨论一下:

  1. 如果位置 i 最终位于上升的山坡上,那么至少需要与其左边所有大于他的数完成一次交换
  2. 如果位置 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;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/108943170