洛谷 P1908 逆序对
法一:归并排序求逆序对(不好理解,记一下)
(此处用的是从大到小排序,毕竟求的是序列中ai>aj且i<j的有序对)
在二路归并的时候,设l<=i<=mid,mid+1<=j<=r,要归并的是a[l]到a[mid]还有a[mid+1]到a[r]。只考虑a[l]到a[r]间产生的逆序对。
在某时刻,要将a[i]或a[j]放入a1[k]位置时,显然i<=k<=j,当a[i]<=a[j]时,不产生逆序对;而a[i]>a[j]时,a[j]放在a[i]之前,a[l]到a[mid]中比a[i]大的数都比a[j]大,这样的数有mid-i+1个,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。
#include<cstdio> int a[40001]; int a1[40001]; int num,n; void merge(int start,int mid,int end) { int k=start,k1=start,k2=mid+1; while(k1<=mid&&k2<=end) { if(a[k1]<=a[k2]) a1[k++]=a[k1++]; else { a1[k++]=a[k2++]; num+=mid+1-k1; } } while(k1<=mid) a1[k++]=a[k1++]; while(k2<=end) a1[k++]=a[k2++]; for(int i=start;i<=end;i++) a[i]=a1[i]; } void merge_sort(int start,int end) { if(start<end) { int mid=(start+end)/2; merge_sort(start,mid); merge_sort(mid+1,end); merge(start,mid,end); } } int main() { int i; scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d",&a[i]); merge_sort(1,n); printf("%d",num); return 0; }
法二:树状数组
#include<cstdio> #include<algorithm> using namespace std; struct Num { int data; int num; friend bool operator<(Num a,Num b) { return a.data<b.data||(a.data==b.data&&a.num<b.num); } }a[50000]; int n; int co[60000]; int shu[240000]; int lowbit(int x) { return x&-x; } void change(int pos,int num) { while (pos<=n) { shu[pos]+=num; pos+=lowbit(pos); } } int sum(int end) { int sum1=0; while (end>0) { sum1+=shu[end]; end-=lowbit(end); } return sum1; } int main() { int i; scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d",&a[i].data); a[i].num=i; } sort(a+1,a+n+1); int id=1; co[a[1].num]=1; for(i=2;i<=n;i++) { if(a[i].data==a[i-1].data) co[a[i].num]=id; else co[a[i].num]=++id; }//至此为止是离散化,就是使各个数字间差更小并且不改变顺序 //例如1,1,2,7,5,9,11可以离散化为1,1,2,4,3,5,6 int ans=0; for(i=1;i<=n;i++) { change(co[i],1); ans+=i-sum(co[i]);//sum(co[i])计算的是树状数组中下标为1~co[i]的值的和,而树状数组中下标为i的值指的是离散化后值为i的数的数量 //因此sum(co[i])计算出的是离散化后小于等于co[i]的数的数量,i-sum(co[i])指的就是离散化后大于co[i]的数的数量 } printf("%d",ans); return 0; }
法三:其他做法
这题不用树状数组的n^2算法是这样的
首先我们把序列存进q数组里,再开一个数组c,然后倒着扫描一遍数组
对于q[i],我们从1~i横扫一遍c数组
如果c[j]为真,说明 j 这个值比
i 这个值先被扫到,即原序列q中, j 在 i 后面
而且我们是从1~i扫的c数组 所以毋庸置疑的j的值比i小
哈哈满足逆序对条件 于是ans+=c[j]
完成之后c[i]++最后输出ans
这题不用树状数组的n^2算法是这样的
首先我们把序列存进q数组里,再开一个数组c,然后倒着扫描一遍数组
对于q[i],我们从1~i横扫一遍c数组
如果c[j]为真,说明 j 这个值比 i 这个值先被扫到,即原序列q中, j 在 i 后面
而且我们是从1~i扫的c数组 所以毋庸置疑的j的值比i小
哈哈满足逆序对条件 于是ans+=c[j]
完成之后c[i]++最后输出ans
首先我们把序列存进q数组里,再开一个数组c,然后倒着扫描一遍数组
对于q[i],我们从1~i横扫一遍c数组
如果c[j]为真,说明 j 这个值比 i 这个值先被扫到,即原序列q中, j 在 i 后面
而且我们是从1~i扫的c数组 所以毋庸置疑的j的值比i小
哈哈满足逆序对条件 于是ans+=c[j]
完成之后c[i]++最后输出ans