逆序对的概念我就不讲了,关于这个求逆序对的问题,通俗的方法自然是要么遍历O(n^2),要么归并排序(nlog n ),那么如何用树状数组实现呢?
下面看一道题目
涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为: ∑(ai-bi)^2
其中 ai 表示第一列火柴中第 i 个火柴的高度,bi 表示第二列火柴中第 i 个火柴的高度。
每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997 取模的结果。
我将基于这题讲下去
首先通过数学证明加上离散化我们可以得到一个数组c[i],含义就是对于a火柴盒中的每一个位置i,在b中本该对应的位置(反过来也可以),那么问题就转化成了在c[i]数组中求逆序对的问题
下面我提供关键代码
void add(int x,int y){//添加 for(;x<=n;x+=x&-x) d[x]+=y; } int ask(int x){//询问前缀和 int y=0; for(;x;x-=x&-x) y+=d[x]; return y; } for(int i=n;i>=1;i--){ add(c[i],1); ans+=ask(c[i]-1); }
最后输出ans就好了
问什么呢???
仔细分析一下
假设在ans+=ask(c[i]-1)之中,现在i=i0,有前缀可以给你加,那么这个前缀是怎么来的呢?
很显然,是通过第一步中来的.那么我们发现,由于i是从n递减,在c[i]中,一定有之前前缀和值个数位于现在的位置之前,并且它们在顺序的数组中的位置还要在c[i0]之前,也就是小于i0。因此我们只需要求前缀和就能知道逆序对的个数。
由此原理计算逆序对