逆序对与本质不同的逆序对

Posted wzx-rs-sthn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了逆序对与本质不同的逆序对相关的知识,希望对你有一定的参考价值。

题目

Description

给定一个序列a1,a2,…,an,如果存在i<j,a[i]>a[j],那么我们称之为逆序对,求逆序对的数目  

Input

第一行为n,表示序列长度,接下来的n行,第i+1行表示序列中的第i个数。  
N<=10^5。Ai<=10^5 

Output

两行,第一行为所有逆序对总数,第二行为本质不同的逆序对总数。

Sample Input

4 
3 
2 
3 
2 

Sample Output

3
1


思路:

由题目可知,逆序对是满足,i<j ,a[i]>a[j] 的两个数,也相当于右边比a[i]小的数与a[i]都构成逆序对;

那么逆序对的个数,就是每一个数右边比它小的数的个数的和;

所以求逆序对就可以用树状数组;

那么什么是本质不同的逆序对呢?就是a[i] a[j]组成的逆序对不与其他的逆序对相同;

比如 3 2 3 2

逆序对的个数有3个,

那么本质不同的逆序对就是3 2,也就只有一个;

这样我们可以用 ff[i] 记录i是否出现过;

这样可以再建一个树状数组,表示不重复的个数;

f[i] 记录i右边比它小的数的个数(并且不重复)同等于在树状数组中求左边比他小的数的个数

通过这个树状数组求f[i]值;

3 2 3 2 1

首先逆循环 ,

读入1, ff[1]==0,1没有出现过,加入树状数组,ff[1]=1,f[1]=0; ans+=f[1]  树状数组:1

读入2,ff[2]==0,没有出现过,加入树状数组,ff[2]=1,f[2]=1;    ans+=f[2]......树状数组:1 2

读入3,ff[3]==0............................................,ff[3]=1,f[3]=2;    ans+=f[3]......树状数组:1 2 3

读入2,ff[2]==1,出现过,不加树状数组.......,f[2]=1;   ans加上答案并且减去以前的f[i]值

也就是减去以前包涵的ans,如3 3 2 ,这两个3都与2组成逆序对,重复了,所以要减去以前包涵的ans

                                                                                                      以前的f[i]值也等于1 ,ans+=1-1;  树状数组:1 2 3

读入3,ff[3]==1............................................,f[3]=2;..................... 以前的f[i]值等于2,ans+=2-2;       树状数组:1 2 3

所以最后的答案是3,本质不同的树状数组是 3 1,3 2,2 1;

 

代码:

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline ll read()
{
    ll a=0,ha=1; char c=getchar();
    while (c<0||c>9) {if (c==-) ha=-1; c=getchar();}
    while (c>=0&&c<=9) {a=a*10+c-0; c=getchar();}
    return a*ha;
}
ll n,m,mx=-1,sum[1000010],f[1000010],ff[1000010];
ll b[1000010],a1[1000010];
struct oh
{
    ll v,num;
}a[1000010];
inline ll lowbit(ll x)
{
    return x&(-x);
}
inline void insert(ll x,ll y)
{
    while(x<=n)
    {
        sum[x]+=y;
        x+=lowbit(x);
    }
}
inline void insert1(ll x,ll y)
{
    while(x<=mx)
    {
        sum[x]+=y;
        x+=lowbit(x);
    }
}
inline ll findout(ll x)
{
    ll ans=0;
    while(x)
    {
        ans+=sum[x];
        x-=lowbit(x);
    }
    return ans;
}
inline ll cmp(oh a,oh b)
{
    if(a.v==b.v)
        return a.num<b.num;
    else
        return a.v<b.v;
}
int main()
{
    n=read();
    for(ll i=1;i<=n;i++)
    {
        a[i].v=read();
        a[i].num=i;
        a1[i]=a[i].v;//记下原数组 用来求本质不同的逆序对, 
        mx=max(mx,a1[i]);//本质不同的逆序对离散化不会,我是大蒟蒻 
    }
    sort(a+1,a+n+1,cmp);
    for(ll i=1;i<=n;i++)
        b[a[i].num]=i;//离散化求逆序对 
    //cout<<endl;
    //for(ll i=1;i<=n;i++)
       // cout<<b[i]<<endl;
    ll ans=0,anss=0;
    for(ll i=n;i>=1;i--)
    {
        ans+=findout(b[i]);
        insert(b[i],1);
    }//逆序对的求法,太水,不予详解
    //--------------以下求本质不同的逆序对,详解看思路------------------ 
    memset(sum,0,sizeof(sum));//重置树状数组 
    for(ll i=n;i>=1;i--)
    {
        if(!ff[a1[i]])//如果没有记录过 
        {
            ll x=findout(a1[i]-1);//查找比他小的个数 
            anss+=x;
            f[a1[i]]=x;   //记下右边有多少数比他小 
            ff[a1[i]]=1;   //标记出现过了 
            insert1(a1[i],1);//加入不重复的树状数组 
        }//如果记录过了就不加树状数组 
        else
        {
            ll x=findout(a1[i]-1);//查找树状数组中有左边多少比它小的数 
            anss+=x-f[a1[i]];//ans减去之前包涵过的 
            f[a1[i]]=x;
        }
    }
    printf("%lld
%lld
",ans,anss);
}

 

以上是关于逆序对与本质不同的逆序对的主要内容,如果未能解决你的问题,请参考以下文章

CodeForces911D 逆序对

求逆序对

求逆序对(树状数组)

一道编程题:求逆序对的个数

逆序对

poj2299(归并排序求逆序对)