/** 鼠标样式 **/

Shu-How Zの小窝

Loading...

逆序对——树状数组

逆序对

题目描述

猫猫 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;
} 
posted @ 2024-09-26 09:35  Violet_fan  阅读(4)  评论(0编辑  收藏  举报