FZU2018级算法第五次作业 m_sort(归并排序或线段树求逆序对)

  首先对某人在未经冰少允许情况下登录冰少账号原模原样复制其代码并且直接提交的赤裸裸剽窃行为,并且最终被评为优秀作业提出抗议!

题目大意:

  给一个数组含n个数(1<=n<=5e5),求使用冒泡排序为递增序列时,最少交换次数为多少次。

题目分析:

  对于数组中任意两个数,若$i<j$且$ai>aj$,则ai与aj必然会进行交换使其变为顺序,因此只需要求逆序对数量即可。

  分享两种方法,第一种使用归并排序:

  对于数组进行递归分治,使用归并排序,在数组两侧合并的过程中,若右侧元素先入数组,则左侧剩余元素均比该元素小,因此可以在归并排序的过程中将逆序对数量求出。

  下面分享代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn=5e5+5;
int arrcy[maxn],lef[maxn],rig[maxn];
ll ans=0;

void mergesort(int *,int ,int);
void merge(int *,int ,int,int);

int main()
{
    int n,i;
    scanf("%d",&n);
    for (i=0;i<n;i++) scanf("%d",&arrcy[i]);
    mergesort(arrcy,0,n-1);
    printf("%lld\n",ans);
    return 0;
}

void mergesort(int arrcy[],int l,int r)
{
    if(l<r)
    {
        int m=(l+r)/2;
        mergesort(arrcy,l,m);
        mergesort(arrcy,m+1,r);
        merge(arrcy,l,m,r);
    }
    return ;
}

void merge(int arrcy[],int l,int m,int r)
{
    int num1=m-l+1,num2=r-m;
    int i,j;
    for (i=0;i<num1;i++) lef[i]=arrcy[l+i];
    for (i=0;i<num2;i++) rig[i]=arrcy[m+1+i];
    i=0,j=0;
    for (int k=0;k<r-l+1;k++)
    {
        if(i>=num1||(j<num2&&lef[i]<=rig[j]))
        {
            arrcy[l+k]=rig[j++];
        }
        else
        {
            if(num2>j)
                ans+=num2-j;
            arrcy[l+k]=lef[i++];
        }
    }
}

  方法二:树状数组或线段树

  先将数组按大小进行离散化排序,从大到小,对于每个元素记录其位置,然后按数组元素大小从大到小,将其原始位置+1,后续操作时先计算1至该元素位置的和,即为与该元素的逆序对数量,进行求和即可。

  分享线段树代码:

  

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair <int,int> pii;
#define rep(i,x,y) for(int i=x;i<y;i++)
#define rept(i,x,y) for(int i=x;i<=y;i++)
#define pb push_back
#define fi first
#define se second
#define mp make_pair
#define dd(x) cout<<#x<<"="<<x<<" ";
#define de(x) cout<<#x<<"="<<x<<"\n";
#define mes(a,b) memset(a,b,sizeof a)

const int maxn=5e5+5;
pii arrcy[maxn];
class Tree
{
    public:
        int l,r;
        ll sum;
}tree[maxn<<2];

void build(int id,int l,int r);
void add(int id,int p,ll num);
ll query(int id,int l,int r);

bool comp(const pii &s1,const pii &s2)
{
    return s1.fi>s2.fi||(s1.fi==s2.fi&&s1.se>s2.se);
}

int main()
{
    int n;
    scanf("%d",&n);
    rept(i,1,n)
    {
        arrcy[i].se=i;
        scanf("%d",&arrcy[i].fi);
    }
    build(1,1,n);
    ll ans=0;
    sort(arrcy+1,arrcy+1+n,comp);
    rept(i,1,n)
    {
        ans+=query(1,1,arrcy[i].se);
        add(1,arrcy[i].se,1);
    }
    printf("%lld\n",ans);
    return 0;
}
void build(int id,int l,int r)
{
    tree[id].l=l;
    tree[id].r=r;
    tree[id].sum=0;
    if(l==r) return ;
    int mid=(l+r)/2;
    build(id*2,l,mid);
    build(id*2+1,mid+1,r);
}
void add(int id,int p,ll num)
{
    if(tree[id].l>=p&&tree[id].r<=p)
    {
        tree[id].sum+=num;
        return ;
    }
    if(tree[id].l>p||tree[id].r<p) return ;
    if(tree[id*2].r>=p) add(id*2,p,num);
    if(tree[id*2+1].l<=p) add(id*2+1,p,num);
    tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;    
}

ll query(int id,int l,int r)
{
    if(tree[id].l>=l&&tree[id].r<=r) return tree[id].sum;
    if(tree[id].l>r||tree[id].r<l) return 0;
    ll s=0;
    if(tree[id*2].r>=l) s+=query(id*2,l,r);
    if(tree[id*2+1].l<=r) s+=query(id*2+1,l,r);
    return s;
}

猜你喜欢

转载自www.cnblogs.com/FZUzyz/p/11723221.html