逆序对 (树状数组 | | 归并排序

数组前面的一个元素 大于等于 后面的一个元素就是一个逆序对;

树状数组可以快速求前缀和,利用这一特性,可以求逆序对个数,见下:

用数组c[ i ]记录数组a[ n ]中i这一元素出现的次数 ,当a[ n ]中元素较大时可以离散化处理。

将a[ n ]从a[n -1]到a[0] 依次存到树状数组中,每存一个,对存的元素i求一次c[i]的前缀和, 这就是当前已扫描过的比i小的元素的个数,由于a[n]是倒着扫描的,所以此时比i小的元素都对应一个逆序对,统计之。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int c[600000];

int a[600000] ,b[600000];
int n;
long long ans;

int lowbit( int x){
    return x & ( -x);
}

void updata( int x ,int v){
     while( x<=n){
         c[x] += v;
         x += lowbit( x);
     }
}

int getsum( int x){
     int s=0;
     while( x>0){
         s += c[ x];
         x -= lowbit( x);
     }
     return s;
}

int main( ){
     while( ~scanf( "%d" ,&n) ,n){
         memset( c ,0 ,sizeof( c ));
         for( int i=0 ;i<n ;i++){
             scanf( "%d" ,&a[i]);
             b[i] = a[i];
         }
         sort( b ,b + n);                        //离散化处理
         int sz = unique( b ,b+n) - b;
         ans = 0;
         for( int i=n-1 ;i >=0  ;i--){
             int t = lower_bound( b ,b+sz ,a[i]) -b +1;  //搜索对应的元素下标
             ans += getsum( t) ;
             updata( t, 1);
         }
         printf( "%lld\n" ,ans);
     }
     return 0;
}

归并排序求逆序对在lrj的高效算法设计一章学过,不过又把一些细节忘了:

1) 分治的参数x,y是左闭右开的,就是(n-1是数组末项下标 (0,n)->( 0,m ),(m ,n )

2)  分治条件y-x>1,因为y是开的,碰不到,y-x==1就是只有一个元素不能再分的情况了

3) 求逆序对时是 cnt += m-p,有半部分有一个小的左半部分没排的(m-p个)都是比他大的

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;

int a[600000];
int b[600000];
int n;
long long ans;

void ergesort( int x ,int y){
   //  cout << x<<‘ ‘<<y<<endl;
     if( y - x <= 1)return;
     int m = x + (y - x)/2;
     ergesort( x ,m) ;
     ergesort( m ,y) ;
     int p = x, q =m ,i=x;

     while ( p < m || q < y){
         if( q >=y || p<m && a[p] < a[q]) b[i++] = a[p++];
         else b[i++] = a[q++] ,ans+= m-p;
     }
     for( int i=x ; i<y ;i++) a[ i] = b[ i];
     return ;
}

int main( ){
     while( ~scanf( "%d" ,&n) ,n){
            ans=0;
         for( int i=0 ;i<n ;i++){
            scanf( "%d" ,&a[i]);
         }
         ergesort( 0 ,n);
  //   for( int i=0 ;i<n ;i++)printf("%d ",a[i]);
         printf( "%lld\n",ans);
     }
     return 0;
}

原文地址:https://www.cnblogs.com/-ifrush/p/10703791.html

时间: 2024-11-10 05:06:08

逆序对 (树状数组 | | 归并排序的相关文章

POJ - 2299 - Ultra-QuickSort = 归并排序 + 逆序对 / 树状数组

http://poj.org/problem?id=2299 求逆序对最简单的绝对不会是树状数组,一定是归并排序(认真),不过树状数组会不会快一点呢?理论上应该是树状数组快一点(假如不进行离散化). #include<algorithm> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<map> #include<set

BZOJ3295 CQOI2011 动态逆序对 树状数组套线段树

离线倒着做,每次加入一个节点后新增的逆序对数量就是其左边大于它的数的个数(左边数的总数-左边小于它的数的个数)+右边小于它的数的个数 用树状数组维护求和,对于树状数组中每个节点v所对应的区间线段树维护区间[l,r]中大于v的数的个数. 最后唯一的问题就是指针版线段树MLE-- #include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> #include <alg

求逆序对[树状数组] jdoj

求逆序对 题目大意:给你一个序列,求逆序对个数. 注释:n<=$10^5$. 此题显然可以跑暴力.想枚举1到n,再求在i的后缀中有多少比i小的,统计答案即可.这显然是$n^2$的.这...显然过不去,我们思考如何优化?显然,这里的有些过程是重复的.我们将这个序列设为a序列,对于两个1到n中的整数i<j,在j后面的数我们进行了多次重复枚举,我们思考如何优化.容易想到用一个桶来记录.只需要记录对于每一个数来讲,我后面有多少个数是比我小的,只需要将桶中的数累加即可.但是,我们必须记录是这个数之后的桶

【2018.12.15】【考试总结】【模拟+逆序对+树状数组+贪心+multiset】爆零之旅

这是我悲惨的接近爆零的一次考试,但是本蒟蒻不能放弃,还是要总结的QAQ 答题卡 [题目背景] 八月是个悲惨的月份.先不谈炎热的天气,对于新生来说,八月意味着军训: 而对于高二高三的同学来说,八月意味着开学考试.而考试就意味着改卷,改卷 也就意味着答题卡.不幸的是,学校读答题卡的机器的评分软件坏了,wyx 就被 老师要求写一个评分的程序. [问题描述] 软件需要读入学生的姓名.试题答案以及学生的答题卡信息. 学生姓名 学校的信息管理系统中存储了所有学生的姓名,一共 名学生.每个学生的 名字的组成只

BZOJ 3295 [Cqoi2011]动态逆序对 树状数组套线段树

题意:链接 方法:树状数组套线段树 解析: 这题基本上写的都是什么CDQ点分治,主席树之类的,然而这我都并不会,所以写了一发平衡树套线段树想卡时卡过去,然而我并没有得逞,T的不要不要的,这里用平衡树套线段树的方法参见我的题解:排队.这道题比那道更要简单. 然后我就打算弃坑了~不过看140142做这道题做的热火朝天的,还是打算回来做一下,yy下树状数组套线段树,然后去看hz的题解,只看懂他写理论部分了,代码部分不知所云,所以还是还是得yy.引用理论部分. 删除某个数,只要统计它之前还存在的比它大的

luogu P1908 逆序对 |树状数组

题目描述 猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计.最近,TOM老猫查阅到一个人类称之为"逆序对"的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对.知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目. Update:数据已加强. 输入格式 第一行,一个数n,表示序列中有n个数. 第二行n个数,表示给定的序列.序列中每个数字不超过10

Inversion (hdu 4911 树状数组 || 归并排序 求逆序对)

Inversion Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 2003    Accepted Submission(s): 787 Problem Description bobo has a sequence a1,a2,-,an. He is allowed to swap two adjacent numbers fo

Day2:T4求逆序对(树状数组+归并排序)

T4: 求逆序对 A[I]为前缀和 推导 (A[J]-A[I])/(J-I)>=M A[j]-A[I]>=M(J-I) A[J]-M*J>=A[I]-M*I 设B[]=A[]-M*(); B[J]>=B[I] 也就是求逆序对: 求逆序对的方法主要有两种: 归并排序: 树状数组: 这里两种方法都学习一下: 1.之前对于树状数组的印象就只有单点修改和区间求和 一直觉得lowbit是一个神奇的东西(至今没有搞懂原理) 上网搜了一下用树状数组求逆序对的方法,发现有一个大神写的很棒....看

【模板】逆序队(树状数组/归并排序)

P1908 逆序对 题目描述 猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计.最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对.知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目. 输入输出格式 输入格式: 第一行,一个数n,表示序列中有n个数. 第二行n个数,表示给定的序列. 输出格式: 给定序列中逆序对的数目