题目地址:POJ 2299
这题以前用归并排序做过。线段树加上离散化也能够做。一般线段树的话会超时。
这题的数字最大到10^10次方,显然太大,可是能够利用下标,下标总共仅仅有50w。能够从数字大的開始向树上加点,然后统计下标比它小即在它左边的数的个数。由于每加一个数的时候。比该数大的数已经加完了,这时候坐标在它左边的就是一对逆序数。
可是该题另一个问题,就是数字反复的问题。这时候能够在排序的时候让下标大的在前面,这样就能够保证加点的时候下标比他小的数中不会出现反复的。
这题须要注意的是要用__int64。
代码例如以下:
#include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #include <math.h> #include <ctype.h> #include <queue> #include <map> #include <set> #include <algorithm> using namespace std; #define LL __int64 #define lson l, mid, rt<<1 #define rson mid+1, r, rt<<1|1 LL sum[2100000]; struct node { LL x, id; } fei[600000]; bool cmp(node x, node y) { if(x.x==y.x) return x.id>y.id; return x.x>y.x; } void PushUp(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void update(LL x, int l, int r, int rt) { if(l==r) { sum[rt]++; return ; } int mid=l+r>>1; if(x<=mid) update(x,lson); else update(x,rson); PushUp(rt); } LL query(int ll, int rr, int l, int r, int rt) { if(ll<=l&&rr>=r) { return sum[rt]; } LL ans=0; int mid=l+r>>1; if(ll<=mid) ans+=query(ll,rr,lson); if(rr>mid) ans+=query(ll,rr,rson); return ans; } int main() { int n, i, j; LL ans; while(scanf("%d",&n)!=EOF&&n) { memset(sum,0,sizeof(sum)); for(i=1; i<=n; i++) { scanf("%I64d",&fei[i].x); fei[i].id=i; } sort(fei+1,fei+n+1,cmp); ans=0; for(i=1; i<=n; i++) { ans+=query(1,fei[i].id,1,n,1); update(fei[i].id,1,n,1); } printf("%I64d ",ans); } return 0; }