当前位置: 首页 > news >正文

逆序对——树状数组

逆序对

题目描述

猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。

最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 a i > a j a_i>a_j ai>aj i < j i<j i<j 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。

输入格式

第一行,一个数 n n n,表示序列中有 n n n个数。

第二行 n n n 个数,表示给定的序列。序列中每个数字不超过 1 0 9 10^9 109

输出格式

输出序列中逆序对的数目。

样例 #1

样例输入 #1

6
5 4 2 6 3 1

样例输出 #1

11

提示

对于 25 % 25\% 25% 的数据, n ≤ 2500 n \leq 2500 n2500

对于 50 % 50\% 50% 的数据, n ≤ 4 × 1 0 4 n \leq 4 \times 10^4 n4×104

对于所有数据, n ≤ 5 × 1 0 5 n \leq 5 \times 10^5 n5×105

思路:

问题解答

Q1: 如何统计第 i i i 个数会与第 i + 1 i+1 i+1 ~ n n n 个数构成多少个逆序对呢?

Ans1: 可以通过建立一个树状数组来解决这个问题。初始时,树状数组的所有元素都为 0 0 0。然后,按照序列从右到左将数据的值对应的位置的数加一,表示该值已经出现。在处理第 i i i 项时,后 n − i n-i ni 项已经加入到树状数组中。树状数组中比 a i a_i ai 小的元素都会与 a i a_i ai 构成逆序对,因为它们出现的更早。因此,产生的逆序对数量为 q u e r y ( a i ) query(a_i) query(ai),其中 q u e r y ( a i ) query(a_i) query(ai) 表示在树状数组内询问 1 1 1 ~ a i a_i ai 项的前缀和。

Q2: 根据 a i a_i ai 来建树状数组空间不够啊?

Ans2: 确实,直接根据 a i a_i ai 的值来建立树状数组可能会导致空间不足。但是,我们只需要数据之间的相对大小,而不需要具体的数值大小。因此,可以通过离散化来解决这个问题。具体来说,先将数据排序,然后用 1 1 1 ~ n n n 分别对应 n n n 个数表示它们的相对大小。这样,对新的序列建立树状数组就足够了,因为 n ≤ 5 × 1 0 5 n \leq 5 \times 10^5 n5×105

Q3: 相等的元素是否会导致求解错误?每一个数(不管是否相等)对应的新数都不同诶?

Ans3: 如果不处理相等的元素,确实会导致错误。问题的关键在于是否有与 a i a_i ai 相等的元素在 a i a_i ai 之前被加入且其相对大小标记更大。为了解决这个问题,可以在排序时将 a i a_i ai 作为第一关键字,下标(第几个出现)作为第二关键字从小到大排序。这样,所有与 a i a_i ai 相等的元素中,先出现的标记也更小,从而避免了误判逆序对的情况。

总结

通过建立树状数组并结合离散化处理,可以高效地统计序列中每个元素与其之前的元素构成的逆序对数量。离散化确保了空间复杂度在可接受范围内,而排序时考虑元素出现顺序则避免了相等元素导致的错误。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+100,mod=998244353;
typedef long long ll;
typedef pair<int,int> PII;
int T;
int n,m;
struct node
{int id;int s;
}a[N],b[N];
bool cmp(node a,node b)
{if(a.s!=b.s) return a.s<b.s;else return a.id<b.id;
}
int c[N];
int ranks[N];
int lowbit(int x)
{return x&(-x);
}
int query(int x)
{int s=0;for(;x>0;x-=lowbit(x)){s+=c[x];}return s;
}
void modify(int id,int x)
{for(;id<=n;id+=lowbit(id)){c[id]+=x;}
}
void solve()
{cin>>n;for(int i=1;i<=n;i++) cin>>a[i].s,a[i].id=i;sort(a+1,a+1+n,cmp);int ans=0;for(int i=1;i<=n;i++) ranks[a[i].id]=i;for(int i=n;i>=1;i--){int idx=ranks[i];ans+=query(idx-1);modify(idx,1);}cout<<ans<<endl;}
signed main()
{ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);T=1;//cin>>T;while(T--){solve();}return 0;
} 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 爬虫-浏览器自动化
  • OceanBase 配置项系统变量实现及应用详解(1):配置项的定义及使用方法
  • 超级好用的java http请求工具
  • shift 命令学习
  • 数据库客户端自定义驱动和数据源:以 HighGo-瀚高为例子
  • 如何分析软件测试中发现的Bug!
  • 快速掌握 ==== js 正则表达式
  • Unity 资源 之 战斗魔法咒语 - 第二卷(Combat Magic Spells - Volume II)
  • 2024年最新PyCharm保姆级安装教程
  • 算法训练(leetcode)第二十八天 | 509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯
  • 通过手机供网、可修改WIFI_MAC的网络设备
  • 相机光学(三十)——N5-N7-N8中性灰
  • UI图标库推荐网站
  • C++继承保姆级讲解下
  • 使用嵌入式知识打造智能手环:nRF52蓝牙开发实战(C++/BLE/传感器)
  • 【css3】浏览器内核及其兼容性
  • AHK 中 = 和 == 等比较运算符的用法
  • echarts的各种常用效果展示
  • FineReport中如何实现自动滚屏效果
  • Javascripit类型转换比较那点事儿,双等号(==)
  • Java编程基础24——递归练习
  • LeetCode18.四数之和 JavaScript
  • leetcode98. Validate Binary Search Tree
  • underscore源码剖析之整体架构
  • vue自定义指令实现v-tap插件
  • Vultr 教程目录
  • 从重复到重用
  • 开源SQL-on-Hadoop系统一览
  • 坑!为什么View.startAnimation不起作用?
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 判断客户端类型,Android,iOS,PC
  • 入门级的git使用指北
  • 什么软件可以提取视频中的音频制作成手机铃声
  • 使用parted解决大于2T的磁盘分区
  • 数据结构java版之冒泡排序及优化
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • Nginx实现动静分离
  • 策略 : 一文教你成为人工智能(AI)领域专家
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • ​【已解决】npm install​卡主不动的情况
  • # Redis 入门到精通(九)-- 主从复制(1)
  • # wps必须要登录激活才能使用吗?
  • # 服务治理中间件详解:Spring Cloud与Dubbo
  • #数学建模# 线性规划问题的Matlab求解
  • (~_~)
  • (13)Latex:基于ΤΕΧ的自动排版系统——写论文必备
  • (el-Transfer)操作(不使用 ts):Element-plus 中 Select 组件动态设置 options 值需求的解决过程
  • (floyd+补集) poj 3275
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (三)uboot源码分析
  • (算法)Game
  • (算法)硬币问题
  • (已更新)关于Visual Studio 2019安装时VS installer无法下载文件,进度条为0,显示网络有问题的解决办法
  • (原创) cocos2dx使用Curl连接网络(客户端)
  • (转)EOS中账户、钱包和密钥的关系