静态求逆序对就是没有修改的序列求逆序对,如果有修改就要用cdq了Orz

  静态求逆序对的方法本弱只会2种,归并排序求逆序对和树状数组求逆序对。复杂度都是nlogn

  归并排序求逆序对

  代码只比普通归并排序多了一句话。归并排序是一直分治下去,直到分到一个小区间里只有2个数的时候在开始排序,而排序的方法是左边和右边都排好队,(以从小到大排序为例),然后每次从2队的队首取出一个较小的元素,放到一个空数组里,这样一直到2队的元素都取完了也就排好了,当然排好的数列存在空数组里,所以还要赋值回来,这样的话,也就保证了,每次排序时的左右2队都是有序的,所以每次取出的2个队里较小的那个也是当前所有未排序元素中最小的那个。举个例子:1 5 8 3,他会分到1 5和8 3,然后1 5 就是这样的顺序,当然排序的时候会比较1和5,然后先1取出来,在把5取出来,3和8同理,然后就变成了1 5 3 8,然后取出1,这时第一队变成5,第二队变成3 8,然后取出3,取出5,取出8,这样就排好啦。

  update:之所以每次只看一半是可行的,是因为前面一半不管内部顺序怎么变,能和后面一半里的数组成逆序对的,变弯还是在那些数的前面,也就是还是可以组成逆序对,后面一半也是如此,而每次合并的时候,由于前后都是已经排好序的(升序),所以当前半部分有一个数a大于后半部分一个数b时,那么a后面的属于前半部分的数都大于b,也就是都可以和b构成逆序对,直接加上这些数的个数就可以了。我纸箱前半部分的指针i和指向后半部分的指针j都是动态的,没次都找出当前最小的元素,然后相应的指针要往后挪,因为如果这个最小元素在前半部分,那么他不会在构成逆序对了,如果他在后半部分,我已经加了前面能和他构成逆序对的数的个数,往后继续找就行了。什么?你问我一个数和自己同属一部分的数构成逆序对的咋办?你忘了归并是分治?我是一直分治到一部分只剩下一个元素才会返回的,这样在他们不属于同意部分的时候我就处理好并把他们排序了。

  而求逆序对的原理就是,左右2边排序不影响统计左边的数和右边的数是不是逆序对,所以当每次较小的那个数是右边那队的队首的时候,左边那队的目前的所有数都比他大,所以所有的数都能和它构成逆序对,统计这个数量,最后求出的就是逆序对数。

  模板:https://www.luogu.org/problemnew/show/P1908

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 int n;
 5 int a[50000],b[50000];
 6 int ans;
 7 void msort(int l,int r)
 8 {
 9     if(l==r)return;
10     int mid=(l+r)>>1;
11     msort(l,mid),msort(mid+1,r);
12     int i=l,j=mid+1,k=l-1;
13     while(i<=mid&&j<=r)
14     {
15         k++;
16         if(a[i]<=a[j])b[k]=a[i],i++;
17         else b[k]=a[j],ans+=mid-i+1,j++;//ans+=mid-i+1就是比归并排序多的那句话
18     }
19     while(i<=mid)b[++k]=a[i],i++;
20     while(j<=r)b[++k]=a[j],j++;
21     for(int i=l;i<=r;i++)a[i]=b[i];
22 }
23 int main()
24 {
25     scanf("%d",&n);
26     for(int i=1;i<=n;++i)scanf("%d",&a[i]);
27     msort(1,n);
28     cout<<ans<<endl;
29     return 0;
30 }
View Code

相关文章: