首页 > 其他分享 >逆序对——树状数组

逆序对——树状数组

时间:2024-09-26 09:35:24浏览次数:1  
标签:树状 int 元素 数组 序列 逆序

逆序对

题目描述

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

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

输入格式

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

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

输出格式

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

样例 #1

样例输入 #1

6
5 4 2 6 3 1

样例输出 #1

11

提示

对于 \(25\%\) 的数据,\(n \leq 2500\)

对于 \(50\%\) 的数据,\(n \leq 4 \times 10^4\)。

对于所有数据,\(n \leq 5 \times 10^5\)

思路:

问题解答

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

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

Q2: 根据 \(a_i\) 来建树状数组空间不够啊?

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

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

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

总结

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

代码:

#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;
} 

标签:树状,int,元素,数组,序列,逆序
From: https://www.cnblogs.com/Violetfan/p/18432789

相关文章

  • 树状数组(Binary Indexed Tree, BIT)
    树状数组(BinaryIndexedTree,BIT)树状数组(BinaryIndexedTree,BIT),也称为FenwickTree,是一种用于高效处理数组前缀和查询和单点更新的数据结构。它能够在(O(\logn))时间内完成单点更新和前缀和查询操作。基本概念前缀和:给定一个数组a,前缀和prefix_sum[i]表示a[0]+......
  • 2535. 数组元素和与数字和的绝对差
    给你一个正整数数组nums。元素和是nums中的所有元素相加求和。数字和是nums中每一个元素的每一数位(重复数位需多次求和)相加求和。返回元素和与数字和的绝对差。注意:两个整数x和y的绝对差定义为|x-y|。示例1:输入:nums=[1,15,6,3]输出:9解释:nums的元......
  • C++——输入一个字符串,把其中的字符按逆序输出。如输入LIGHT,输出THGIL。用string方法
    没注释的源代码#include<iostream>#include<string.h>usingnamespacestd;intmain(){   stringa;   cout<<"请输入字符串a:";   cin>>a;   intk;   k=a.size();   for(inti=k-1;i>=0;i--)   {       cout<<a[i];......
  • javascript向数组添加元素
    javascript向数组添加元素,比较常用的是两种方法,一种是向数组后面添加元素,一种是在数组前面添加元素。向数组后面添加元素,一般用push语句,它返回的是添加新元素之后的数组长度。push语法格式是数组名.push('要添加的数组元素')比如有一个数组名字叫arr,要向数组后面添加一个'g......
  • Go从入门到放弃之数组、切片
    一、数组数组的声明和初始化在Go语言中,数组是固定长度的、同一类型的数据集合。数组中包含的每个数据项被称为数组元素,一个数组包含的元素个数被称为数组的长度。在Go语言中,你可以通过 [] 来标识数组类型,但需要指定长度和元素类型,使用时可以修改数组成员,但是数组大小不可变化......
  • 2024-09-25:用go语言,给定一个长度为 n 的整数数组 nums 和一个正整数 k, 定义数组的“能
    2024-09-25:用go语言,给定一个长度为n的整数数组nums和一个正整数k,定义数组的"能量"为所有和为k的子序列的数量之和。请计算nums数组中所有子序列的能量和,并对结果取模10^9+7后返回。输入:nums=[1,2,3],k=3。输出:6。解释:总共有5个能量不为0的子序列:子......
  • 一维数组的创建和初始化
    当变量出现,我们就有了存放单个数据的概念,那么我们有一堆数据呢?比如:我们班的数学成绩有30个数据,此时我们可以把它们看作一个集体C语言就出现了数组的概念,创建一个连续的空间将同类型的多个数据存放在一起,并且可以指定大小,就是数组。1.数组的概念数组就是存放着同类型元素的......
  • C语言数组探秘:数据操控的艺术【上】
    在C语言中数组是非常重要的,应用也是非常广泛的,它可以帮助我们更好的写代码,来解决问题。欧克,开始今天的数组的章节。一.数组的概念数组是一组相同类型元素的集合;从这个概念中我们就可以发现2个有价值的信息:数组中存放的是1个或者多个数据,但是数组元素个数不能为0。数组......
  • 一维数组的使用
    存放数组的目的是对数据进行操作,那么今天我们,来讲讲对数组的使用,希望我的理解可以帮助到同样是小白的你。1.数组的下标在C语言中规定了数组是有下标的,从零开始,如果有n个元素,那么就有n-1个下表,下标也可以说是元素的编号。1.intarr[5]={1,2,3,4,5};    下标   ......
  • C#|.net core 基础 - 扩展数组添加删除性能最好的方法
    C#|.netcore基础-扩展数组添加删除性能最好的方法 合集-C#|.netcore基础(6)  今天在编码的时候遇到了一个问题,需要对数组变量添加新元素和删除元素,因为数组是固定大小的,因此对新增和删除并不友好,但有时候又会用到,因此想针对数组封装两个扩展方法:新增元素与......