解题思路:
先熟悉树状数组原理及其应用。
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; }
0.0分
20 人评分
简单的a+b (C语言代码)浏览:564 |
数组输出 (C语言代码)错误???浏览:602 |
Wu-求圆的面积 (C++代码)浏览:1994 |
简单的a+b (C语言代码)浏览:618 |
C语言程序设计教程(第三版)课后习题10.5 (C语言代码)浏览:985 |
2003年秋浙江省计算机等级考试二级C 编程题(1) (C语言代码)浏览:639 |
C语言程序设计教程(第三版)课后习题1.5 (C语言代码)浏览:527 |
C语言程序设计教程(第三版)课后习题1.5 (C语言代码)浏览:477 |
2005年春浙江省计算机等级考试二级C 编程题(2) (C语言描述之函数调用)浏览:835 |
世界杯来了浏览:1006 |