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; } }
浙公网安备 33010602011771号