G - 逆序对的数量

原题链接

什么是逆序对?

简单来说,两个数比较,下标小的数反而大,两个数称之为逆序对如\({3,1}\)就是这么一个逆序对

归并排序

由于逆序对逆序的性质,我们可以联想到排序:

排序的过程就是消除逆序对的过程,消除的次数就是逆序对的数量

归并排序的性质:每次划分后合并时左右子区间都是从小到大排好序的,我们只需要统计右边区间每一个数分别会与左边区间产生多少逆序对即可

注意

逆序对的个数最大的情况发生在整个数组逆序时即:

\[(n-1)+(n-2)+...+1 = \frac{n\cdot(n-1)}{2}
\]

由于

\[n≤5×10^5
\]

答案是大于\(10^{10}\)的(会爆int)

注意要使用long long

代码1

点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
using namespace std; #define X first
#define Y second typedef pair<int,int> pii;
typedef long long LL;
const char nl = '\n';
const int N = 5e5+10;
const int M = 2e5+10;
int n,m;
int q[N],temp[N]; LL merge_sort(int l,int r){
if(l >= r)return 0; int mid = l + r >> 1;
LL res = merge_sort(l,mid) + merge_sort(mid+1,r); int k = 0,i = l,j = mid + 1;
while(i <= mid && j <= r){
if(q[i] <= q[j])temp[++k] = q[i++];
else{
temp[++k] = q[j++];
res += mid - i +1;
}
}
while(i <= mid)temp[++k] = q[i++];
while(j <= r)temp[++k] = q[j++]; for(int i = l,k = 1; i <= r; i ++,k ++)q[i] = temp[k];
return res;
} void solve(){
cin >> n;
for(int i = 1; i <= n; i ++ )cin >> q[i];
cout << merge_sort(1,n) << nl; } int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0); solve();
}

树状数组

树状数组相关知识以及模板题1

树状数组相关知识以及模板题2

思路来源于暴力解法:从小到大枚举数组的每一个数(天然地形成逆序对\(j<i\)(j指的是先前的数,i指的是当前的数)的条件),此时我们只需要知道前面有多少个数比a_i(当前数)小即可,这里我们可以用到(就是一个数组cnt)来记录每个数的出现(怎么记录?每次枚举到这个数后,\(cnt[a_i]++\)即可)

然而,\(a_i\)的范围是\(10^9\),而\(n\)的范围却只有\(5\cdot10^5\),直接开cnt数组会mle

这时,我们又可以发现逆序对只跟相对大小有关系,所以我们的第一步优化便是将原数组离散化处理(排序+去重)得到每个数的相对大小,同时每次枚举时使用二分来找到每个数的相对大小即可

然而,此时我们又遇到一个问题,对于每次统计前面有多少个数比a_i(当前数)小又需要多次求和,暴力解又会tle

这时,我们又可以联想到多次求和有奇效的树状数组,通过树状数组来维护桶

答案显而易见

\[\sum_{i = 1}^{n} \ ( \ i \ - \ getsum(1,k) \ )
\]

\(getsum(1,k)\)为小于等于\(a_i\)的数的数量(等于的话不是逆序对)

\(i - getsum(1,k)\)得到的则时关于\(a_i\)逆序对的数量

代码

点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
using namespace std; #define X first
#define Y second typedef pair<int,int> pii;
typedef long long LL;
const char nl = '\n';
const int N = 1e6+10;
const int M = 2e5+10;
int n,m;
int a[N],b[N]; //原数组,树状数组(维护桶)
vector<int> v; //储存所有待离散化的值进行排序和去重
LL ans = 0; int lowbit(int x){
return x & -x;
} void add(int k,int x){
while(k <= n){
b[k] += x;
k += lowbit(k);
}
} LL getsum(int l,int r){
LL s1 = 0;
l --;
while(l){
s1 += b[l];
l -= lowbit(l);
}
LL s2 = 0;
while(r){
s2 += b[r];
r -= lowbit(r);
}
return s2 - s1;
} int find(int x){ //找到a[i]在序列中排第几
int l = 0,r = v.size() - 1;
while(l < r){
int mid = l + r >> 1;
if(v[mid] >= x)r = mid;
else l = mid + 1;
}
return l + 1; //v从0开始
//此处映射为1,2,3,...,n
} void solve(){
cin >> n;
for(int i = 1; i <= n; i ++ ){
cin >> a[i];
v.push_back(a[i]);
} sort(v.begin(),v.end()); //排序(从小到大)
v.erase(unique(v.begin(),v.end()),v.end()); //去重
// //离散化得到每个数的相对大小 //枚举每个数找逆序对数量
for(int i = 1; i <= n; i ++ ){
int k = find(a[i]);
add(k,1);
ans += (i - getsum(1,k)); //getsum(1,k)为小于等于a[i]的数的数量(等于的话不是逆序对)
}
cout << ans << nl;
} int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0); solve();
}

G - 逆序对的数量的更多相关文章

  1. [算法导论]练习2-4.d求排列中逆序对的数量

    转载请注明:http://www.cnblogs.com/StartoverX/p/4283186.html 题目:给出一个确定在n个不同元素的任何排列中逆序对数量的算法,最坏情况需要Θ(nlgn)时 ...

  2. 求数组中的逆序对的数量----剑指offer36题

    在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数: 如数组{7,5,6,4},逆序对总共有5对,{7,5},{7,6},{7, ...

  3. [AcWing 788] 逆序对的数量

    点击查看代码 #include<iostream> using namespace std; typedef long long ll; const int N = 1e5 + 10; i ...

  4. 用归并排序或树状数组求逆序对数量 poj2299

    题目链接:https://vjudge.net/problem/POJ-2299 推荐讲解树状数组的博客:https://blog.csdn.net/int64ago/article/details/ ...

  5. Codeforces 987 K预处理BFS 3n,7n+1随机结论题/不动点逆序对 X&Y=0连边DFS求连通块数目

    A /*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) #define pb push_bac ...

  6. lintcode:逆序对

    题目 在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.给你一个数组,求出这个数组中逆序对的总数.概括:如果a[i] > a[j] 且 i < j, a[i] ...

  7. 洛谷 P1908 逆序对

    \[传送门qwq\] 题目描述 猫猫\(TOM\)和小老鼠\(JERRY\)最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计. 最近,\(TOM\)老猫查阅 ...

  8. 牛客练习赛38 D 题 出题人的手环 (离散化+树状数组求逆序对+前缀和)

    链接:https://ac.nowcoder.com/acm/contest/358/D来源:牛客网 出题人的手环 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 524288K,其他 ...

  9. 剑指Offer 35. 数组中的逆序对 (数组)

    题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%1000 ...

  10. 洛谷P1908 逆序对【递归】

    题目:https://www.luogu.org/problemnew/show/P1908 题意:给定一个数组,求逆序对个数. 思路: 是一个很经典的题目了.通过归并排序可以求逆序对个数. 现在有一 ...

随机推荐

  1. openstack单机部署 未完成

    注:centos8单机版 注:本次实验手动配置密码均为admin 环境准备:配置hosts文件 192.168.116.85为本机IP echo '192.168.116.85 controller ...

  2. 【Java并发009】原理层面:ThreadLocal类全解析

    一.前言 在Java多线程模块中,ThreadLocal是比较重要的知识点,虽然ThreadLocal类位于java.lang包,但是这个类基本上仅用于多线程. 二.ThreadLocal类概要 2. ...

  3. float16与float32转换

    // based on https://gist.github.com/martin-kallman/5049614 // float32 // Martin Kallman // // Fast h ...

  4. C温故补缺(十二):预编译器与头文件

    预编译器 预编译器就是之前学的预编译指令的执行者 gcc -E test.c -o test.i 生成预编译文件就是翻译#指令 比如#include<stdio.h>就是把整个stdio. ...

  5. WINDOWS下对NIGNX日志文件进行限制

    首先接到这个任务,发现nginx的日志限制更多的都是在Linux下做的,找了半天,也没找到能直接通过nginx.conf更改体现到日志限制上的. 最后决定直接通过bat脚本,来对nginx的日志进行分 ...

  6. JDK 8 Stream 流 用 法

    import com.entity.Person;import org.junit.Test;import java.util.*;import java.util.function.Function ...

  7. 【2022-11-28】Docker部署搭建Gitlab

    一.环境准备 1. 准备一台虚拟机\或者购买服务器 2. 虚拟机硬件要求 2.1 内存不得少于4G,否则启动会报502错误,可自行百度解决,将虚拟机的swap分区调整为2G大小即可 2.2 CPU2核 ...

  8. Pytorch框架详解之一

    Pytorch基础操作 numpy基础操作 定义数组(一维与多维) 寻找最大值 维度上升与维度下降 数组计算 矩阵reshape 矩阵维度转换 代码实现 import numpy as np a = ...

  9. JavaScript:操作符:逗号运算符

    逗号运算符,是极少见的运算符,我们看一下代码理解一下逗号运算符的功能: 先说结论,逗号运算符的优先级非常低,比赋值运算符=还要低: 同时,逗号隔开的几个表达式,都会各自进行计算,但是整体表达式只会返回 ...

  10. DFS深度优先搜索例题分析

    洛谷P1596 Lake Counting S 题面翻译 由于近期的降雨,雨水汇集在农民约翰的田地不同的地方.我们用一个 \(N\times M(1\times N\times 100, 1\leq ...