求逆序数对。
看了一下别人的题解,发现这题还可以用树状数组的方法来做。
因为数据大小范围为0~999,999,999,若用此数作为数组下标肯定会超出内存限制,所以采用离散化的方法先将数据范围缩小。
比如9,1,4,3,5可以变为5,1,3,2,4来处理,这样数组最大的下标就是5,大大缩小了数据范围,因为n<500,000,所以数组开500,000就够了。
思路是从头到尾循环一次,假设循环变量为pos,即当前位置为pos,则对C[i]求和表示1~pos的范围内存在多少个比i小的数,而题目要求是逆序数对,则用对C[n]求和减去对C[i]求和即可表示i前面存在了多少个比它大的数,以上面的5,1,3,2,4为例子:
pos为1时,num[pos]=5,getsum(C[num[pos]])=0,getsum(C[n])=0,getsum(C[n])-getsum(C[num[pos]])=0,
pos为2时,num[pos]=1,getsum(C[num[pos]])=0,getsum(C[n])=1,getsum(C[n])-getsum(C[num[pos]])=1,
pos为3时,num[pos]=3,getsum(C[num[pos]])=1,getsum(C[n])=2,getsum(C[n])-getsum(C[num[pos]])=1,
pos为4时,num[pos]=2,getsum(C[num[pos]])=1,getsum(C[n])=3,getsum(C[n])-getsum(C[num[pos]])=2,
pos为5时,num[pos]=4,getsum(C[num[pos]])=3,getsum(C[n])=4,getsum(C[n])-getsum(C[num[pos]])=1,
答案即为0+1+1+2+1=5,可验证一下逆序数对为(5,1),(5,3),(5,2),(3,2),(5,4)共五对。
树状数组的求解过程:
如果使用同样的方法而不用树状数组的话,用C[i]表示1~pos的范围内存在多少个比i小的数,虽然求和效率高,但是增加元素时效率很低,比如说,在进行到pos=2的时候,需要修改的元素有C[2],C[3],……,C[n],效率极低。用树状数组虽然求和效率低,但是增加元素时效率高,合起来的效率也高很多,因而用树状数组更好。
不用树状数组的数组变化:(同样是5,1,3,2,4的例子)
C[1]=0,C[2]=0,C[3]=0,C[4]=0,C[5]=0, 5~5有0对
C[1]=0,C[2]=0,C[3]=0,C[4]=0,C[5]=1, 5~1有C[5]-C[1]=1对
C[1]=1,C[2]=1,C[3]=1,C[4]=1,C[5]=2, 5~3有C[5]-C[3]=1对
C[1]=1,C[2]=1,C[3]=2,C[4]=2,C[5]=3, 5~2有C[5]-C[2]=2对
C[1]=1,C[2]=2,C[3]=3,C[4]=3,C[5]=4, 5~4有C[5]-C[4]=1对
C[1]=1,C[2]=2,C[3]=3,C[4]=4,C[5]=5, (这一组数据实际上没有用到)
0+1+1+2+1=5对
附上转自http://hi.baidu.com/lewutian/item/495088a9bb2d0e3f020a4d3c的代码:(与上面描述的变量名等可能有所不同)
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define M 500005 int cc[M],n,num[M]; struct D { int val,no; }data[M]; bool cmp(D a,D b) { return a.val<b.val; } int lowbit(int x) { return x&(-x); } void up(int x,int nu) { while(x<=n) { cc[x]+=nu; x+=lowbit(x); } } int getsum(int x) { int sum=0; while(x) { sum+=cc[x]; x-=lowbit(x); } return sum; } int main() { int i; long long ans; while(scanf("%d",&n)&&n) { memset(cc,0,sizeof(cc)); for(i=0;i<n;i++) { scanf("%d",&data[i].val); data[i].no=i; } sort(data,data+n,cmp); for(i=0;i<n;i++) num[data[i].no]=i+1; for(i=0,ans=0;i<n;i++) { ans+=(getsum(n)-getsum(num[i])); up(num[i],1); } printf("%lld\n",ans); } }