zoukankan      html  css  js  c++  java
  • 求逆序对与本质不同的逆序对

    题目

    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


    下面链接教你怎么求有多少个在a前面的数比a小(如果不会一定要看,与题目求逆序对有关)

    https://www.cnblogs.com/wzx-RS-STHN/p/13193271.html

    思路:

    由题目可知,逆序对是满足,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);
    }
  • 相关阅读:
    C#博客记录二
    C#博客记录一
    label语句
    css选择器
    关于访问对象属性的小问题
    特殊符号unicode编码
    不换行
    正则表达式中的exec()方法
    正则表达式中两种定义方式中的反斜杠
    js删除对象数组
  • 原文地址:https://www.cnblogs.com/wzx-RS-STHN/p/13192762.html
Copyright © 2011-2022 走看看