POJ 2299

  这是一道树状数组的应用题,题意是要对一个序列按升序排序,每次交换相邻两个数,求交换次数。假设a,b,c相邻,如果a>b,a>c,那么a,b就应该先交换一次,a和c再交换一次。所以这道题就抽象为求逆序数,即对每个i,找到a[i]的左边比a[i]大的数的个数,求出总和。由于树状数组可以很方便地求出一个数左边比它小的数的个数,所以进一步转化为:对每个i,找到从a[i]开始,到最左边小于等于a[i]的数的个数,然后用i减去这个数,就得到a[i]左边比a[i]大的数的个数。

  具体来说,就是维护一个数组(c[]),初始化所有值为0,对需要统计逆序数的序列,从左向右依次加入序列中的点,比如序列a,b,c,d,首先加入a,就将c[a]赋值为1,然后求c[]中,c[1]+c[2]+……+c[a],得到的就是序列中a的左边(包括a)小于等于a的数的个数,当然,要保证加入的数大于0,否则就要从c[0]开始加。可以看到,每次都要求一个数组中从第1项到第a项的和,这就是要使用树状数组的原因。

  另外,为了节省c[]的空间,要对给出的序列连续化,比如给出序列1,100,1000,将c开到1000是不经济的,于是对这个间断的序列连续化为1,2,3,就能够节省空间了。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAX_N 500005
struct node
{
    int val;
    int num;
}recrd[MAX_N];
int data[MAX_N],c[MAX_N],n;
int cmp(const void *,const void *);
int get_sum(int);//树状数组的求区间和的函数
inline int lowbit(int);
void update(int,int);//更新树状数组中的值
int main()
{
    while(scanf("%d",&n)!=EOF&&n!=0)
    {
    int i;
  /******************************************/
  /**读入序列,并且将这些序列压缩为连续的序列******/
for(i=1;i<=n;i++) { scanf("%d",&recrd[i].val); recrd[i].num=i; } qsort(&recrd[1],n,sizeof(struct node),cmp); for(i=1;i<=n;i++) { data[(recrd[i].num)]=i; }
  /**********压缩结束************************/
  /*****************************************/ memset(c,
0,sizeof(c)); long long ans=0; for(i=1;i<=n;i++) { update(data[i],1); ans+=i-get_sum(data[i]); } printf("%lld\n",ans); } return 0; } int cmp(const void *x,const void *y) { return (*(struct node *)x).val-(*(struct node *)y).val; } inline int lowbit(int x) { return x&(-x); } int get_sum(int x) { int i,temp=0; for(i=x;i>0;i-=lowbit(i)) { temp+=c[i]; } return temp; } void update(int x,int val) { int i; for(i=x;i<=n;i+=lowbit(i)) { c[i]+=val; } }
posted @ 2012-08-08 16:00  等待电子的砹  阅读(137)  评论(0)    收藏  举报