树状数组(归并排序) 之 求逆序数nyoj117

题目信息:点击打开链接 求逆序数

树状数组求逆序数就是不停的向树状数组中插入值,插入的同时更新树状数组,并统计逆序数的个数,在代码中会详细解释;另外大家会发现这种方法很类似于hash标记,即先开一个hash数组(和题上数据范围一样大),然后插入值a,然后标记hash[a]为1,假如a是第5个插入的,那a的逆序数就是5-get_sum(C[4])(C为树状数组),但是这道题的数据范围为10^9,根本开不了这麽大的数组,那就讲到了  离散化处理,假如有这样一组数5 3 2 7 999999999,如果直接哈希开数组要开到hash[999999999];但这在实际中是开不到的,也完全没必要开这麽大,我们求得是逆序数,也就是只和每个数的相对大小有关系,那3 2 1 4 5和上面那组数的逆序数是一样的,离散化就完成两个数组之间的转化,转化之后hash数组只需要hash[5]就行。

代码如下:

#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
//用于离散化处理
struct node{
int v;//数值
int w;//出现位置
}s[1000005];
long long c[1000005],tree[1000005];
int lowbit(int x)
{
    return x&(-x);
}
void insert(int x,int y,int n)
{
    while(x<=n)
    {
        tree[x]+=y;
        x+=lowbit(x);
    }
}
int get_sum(int x)
{
    int sum=0;
    while(x>0)
    {
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}
bool cmp(node s1,node s2)
{
    if(s1.v!=s2.v)
    return s1.v<s2.v;
    return s1.w<s2.w;//由于一组数据中有相同数据,故需稳定排序
}
int main()
{
    int T,N,i,j;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&N);
        for(i=1;i<=N;i++)
        {
            scanf("%lld",&s[i].v);
            s[i].w=i;
        }
        sort(s+1,s+N+1,cmp);//稳定排序
        for(i=1;i<=N;i++)//离散化处理
            c[s[i].w]=i;
        memset(tree,0,sizeof(tree));//初始化树状数组
        long long ans=0;
        for(i=1;i<=N;i++)
        {
            insert(c[i],1,N);
            ans+=(i-get_sum(c[i]));//求逆序数,当前插入次数i-当前插入位置前面的数总个数get_sum(c[i]);
        }
        printf("%lld\n",ans);
    }
}

另外前面的博客也说道归并排序求逆序数,代码如下:

#include<stdio.h>
#include<string.h>
#define N 1000005
long long cnt=0;
int a[N],c[N];
//归并排序的合并操作
int merge(int a[],int first,int mid,int last,int c[])
{
    int i=first,j=mid+1;
    int m=mid,n=last;
    int k=0;
    while(i<=m||j<=n)
    {
        if(j>n||(i<=m&&a[i]<=a[j]))
            c[k++]=a[i++];
        else
            {c[k++]=a[j++];cnt+=(m-i+1);}
    }
    for(i=0;i<k;i++)
        a[first+i]=c[i];
}
//归并排序的递归分解和合并
void merge_sort(int a[],int first,int last,int c[])
{
    if(first<last)
    {
        int mid=(first+last)/2;
        merge_sort(a,first,mid,c);
        merge_sort(a,mid+1,last,c);
        merge(a,first,mid,last,c);
    }
}
int main()
{
    int T,M,i;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&M);
        memset(a,0,sizeof(a));
        memset(c,0,sizeof(c));
        cnt=0;
        for(i=0;i<M;i++)
            scanf("%d",&a[i]);
        merge_sort(a,0,M-1,c);
        printf("%lld\n",cnt);
    }
}

同样的题,树状数组写的跑了1200ms,归并排序才跑600ms不到。。。



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值