VJ Ultra-QuickSort (逆序对)

原题

逆序对定义:
对于一个序列 a,若 i < j 且 a[i] > a[j],则称 a[i] 与 a[j] 构成逆序对

如何使用树状数组求序列逆序对个数:
任意给定一个集合 a,如果用 c[val] 保存数值 val 在集合 a 中出现的次数,那么数组 c 在 [l,r] 上的区间和就表示集合 a 在范围 [l,r]内的数有多少个。

  1. 在序列 a 的数值范围上建立树状数组 c[N],初始化全为零。
  2. 倒序扫描给定的序列 a,对于每个数 a[i]:
    1.在树状数组中查询前缀和[1,a[i]-1](我们只需知道在a[i]之前有多少个数,就知道有多少个逆序对),累加到答案中。
    2.执行“单点增加”操作,即把位置 a[i] 上的数加1(相当于c[a[i]]++,a[i]这个数出现次数加一),同时正确维护 c 的前缀和。
  3. ans即为所求
   for(int i=n;i>=1;i--)
{
     ans+=ask(a[i]-1);//因为查询的是a[i]-1,所以该行代码可与add操作互换位置
     add(a[i],1);
}

根据以上思路,得到如下代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
ll n,a[500005],c[500005],maxn;
ll ask(ll x)
{
    ll ans=0;
    for(; x; x-=x&-x)
    {
        ans+=c[x];
    }
    return ans;
}
void add(ll x,ll y)
{
    for(; x<=maxn; x+=x&-x)//注意<=maxn
    {
        c[x]+=y;
    }
}
int main()
{
    while(cin>>n,n)
    {
           maxn=0;
        for(ll i=1; i<=n; i++)
        {
            scanf("%lld",&a[i]);
            a[i]+=1;
            maxn=max(maxn,a[i]);//记录最大的数maxn,因为上面提到树状数组是在数值范围上建立的
        }
        ll ans=0;
        for(ll i=n; i; i--)
        {
            add(a[i],1);
            ans+=ask(a[i]-1);
        }
        cout<<ans<<endl;
        memset(c,0,sizeof(c));//注意初始化
    }
    return 0;
}

测下样例,完美!交一发,RE?
原来这题a[i]的数据范围特别大,树状数组也无法开到这么大的空间,所以得先对数据离散化处理

我们可以再开个数组d[N]用来记录a[i],然后将d[N]从小到大排序,再用lower_bound()函数进行查询,得到a[i]的相对位置(数组下标)

AC代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<set>
using namespace std;
typedef long long ll;
ll n,a[500005],c[500005],d[500005],maxn;
ll ask(ll x)
{
    ll ans=0;
    for(; x; x-=x&-x)
    {
        ans+=c[x];
    }
    return ans;
}
void add(ll x,ll y)
{
    for(; x<=n; x+=x&-x)//注意这里树状数组的数值范围是n
    {
        c[x]+=y;
    }
}
int main()
{
    while(cin>>n,n)
    {
        for(ll i=1; i<=n; i++)
        {
            scanf("%lld",&a[i]);
            d[i]=a[i];
        }
        ll ans=0;
        sort(d+1,d+n+1);
        for(ll i=n; i>=1; i--)
        {
            int x=lower_bound(d+1,d+n+1,a[i])-d;
            add(x,1);
            ans+=ask(x-1);
        }
        cout<<ans<<endl;
        memset(c,0,sizeof(c));
    }
    return 0;
}

当然也可以开个结构体,然后排序,总之离散化的方法有很多。还有数据范围要开到ll

戒骄戒躁,百炼成钢!
原文地址:https://www.cnblogs.com/Pecoz/p/12404346.html