解题思路:

    先熟悉树状数组原理及其应用。
    1.这道题可以转换成求每个位置的左边比他小的个数和右边比他大的个数,这两个相加就是这个人要被交换的次数,然后根据等差数列前n项求和公式(a1+an)*n/2,将所有位置和相加即可。

    2.求逆序数用树状数组优化成 nlogm,树状数组是专门用来求前缀和的,这里从位置0遍历到n-1,把高度作为树状数组的下标,对于输入的小朋友高度a[i],在当前小朋友左边&&比当前小朋友a[i]高的总数,就是树状数组s(maxh)-s(a[i])=i+1-s(a[i]),反之同理。
注意事项:
    1.题中a[i]可以等于0,所以要把输入加一,不然在后面计算sum(a[i]-1)处,会越界。

    2.代码中a数组是保存输入,d数组的下标是高度,lr数组是保存位置i的左右逆序总数,maxh是输入的最大值

参考代码:

#include <iostream>
#include <cstdio>
#define _for(i,a,b) for(int i=a;i<b;i++)
#define _unfor(i,a,b) for(int i=a;i>=b;i--)
#define mset(a,val,n) for(int i=0;i<n;i++)a[i]=val;
#define lowbit(x) (x&(-x))
using namespace std;
typedef long long LL;

int a[100005],d[1000005],n,lr[100005],maxh=0;
//tree_arr
    void add(int i,int x){
        while(i<=maxh+1)d[i]+=x,i+=lowbit(i);
    }

    int sum(int i){ 
        int res=0;
        while(i>0)res+=d[i],i-=lowbit(i);
        return res;
    }
//
int main(){
    scanf("%d",&n);
    _for(i,0,n){scanf("%d",&a[i]);maxh=max(maxh,++a[i]);}
    _for(i,0,n){ //求所有位置的左边的逆序数
        add(a[i],1);
        lr[i]+=i+1-sum(a[i]);
    }
    mset(d,0,maxh+5);
    _unfor(i,n-1,0){//求所有位置的右边的逆序数
        add(a[i],1);z
        lr[i]+=sum(a[i]-1);
    }
    LL ans=0;
    _for(i,0,n){
        LL k=lr[i];
        ans+=k*(k+1)/2;//等差数列求和公式(中间变量需要用LL保存)
    }
    cout<<ans<<endl;
}


点赞(8)
 

0.0分

13 人评分

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

评论列表 共有 3 条评论

不看答案就ac 2年前 回复TA
@验题君 算法打榜比赛中,大佬们经常这么做,提高书写速度
Lucy 4年前 回复TA
是左边有多少个比它大的,右边有多少个比它小的之和吧?
验题君 6年前 回复TA
这个宏定义用的很6