树状数组求逆序对
对于数的范围比较小,我们可以这样来求解逆序对。
树状数组b[val]表示的是val在数组中出现的次数。
我们倒序扫描原数组a,对于位置i,由于树状数组里面保存的是val出现的次数,我们先用树状数组求出当前树状数组中比a[i]这个值小的元素的个数,由于是倒序扫描,之前加入树状数组中的数的位置都在i后面,因此我们就把求得的比a[i]这个值小的元素的个数累加到答案中。
for(int i=n;i;i--) { ans+=ask(a[i]-1); add(a[i],1); }
这是针对数的范围较小采取的方法,当数的范围较大时,我们对数据经行排序,然后离散化。下面给出一种求解方法。
#include<algorithm> #include<cstdio> #include<cstring> #include<iostream> using namespace std; const int N = 5e5 + 10; typedef long long ll; #define lowbit(x) (x&(-x)) int n, m, c[N], a[N], b[N]; void add(int x) { for (; x <= n; x += lowbit(x)) c[x]++; } int ask(int x) { int s = 0; for (; x > 0; x -= lowbit(x)) //cout << x <<" "<< c[x] << endl; s += c[x]; return s; } bool cmp1(const int x, const int y) { if (b[x] == b[y]) return x > y;//这个要注意,一定要让大的跑前面,不然就会多统计 return b[x]>b[y]; } int main() { scanf("%d", &n); ll ans = 0; for (int i = 1; i <= n; i++) { scanf("%d", &b[i]); a[i] = i; } sort(a + 1, a + n + 1, cmp1); for (int i = 1; i <= n; i++) { add(a[i]); ans += 1LL * ask(a[i] - 1);//之前加入数状数组的元素都比当前元素值大,因此我们查询下标比当前小的个数,累加答案 } printf("%lld\n", ans); return 0; }
这种情况需要先排序,那还不如直接用归并排序求。