nyist oj 117 求逆序数 (归并排序&&树状数组)

求逆序数

时间限制:2000 ms  |  内存限制:65535 KB

难度:5

描述

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。

现在,给你一个N个元素的序列,请你判断出它的逆序数是多少。

比如 1 3 2 的逆序数就是1。

输入
第一行输入一个整数T表示测试数据的组数(1<=T<=5)

每组测试数据的每一行是一个整数N表示数列中共有N个元素(2〈=N〈=1000000)

随后的一行共有N个整数Ai(0<=Ai<1000000000),表示数列中的所有元素。

数据保证在多组测试数据中,多于10万个数的测试数据最多只有一组。

输出
输出该数列的逆序数
样例输入
2
2
1 1
3
1 3 2
样例输出
0
1
来源
[张云聪]原创
上传者
张云聪

求逆序数:用归并排序来做,效率还是很高的;

归并排序利用分治的思想,先把一个数组分成一个个序列,然后对一个个序列排序,把排好序的序列,在合并到原来的数组中。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1000001;
int a[maxn],b[maxn];
long long sum;
void Merge(int begin,int mid,int end)//归并
{
    int i=begin,j=mid+1,pos=begin;
    while(i<=mid && j<=end)//对一个个序列排序的过程
    {
        if(a[i]<=a[j])
        {
            b[pos++]=a[i++];
        }
        else
        {
            b[pos++]=a[j++];
            sum+=mid-i+1;//求逆序数
        }
    }
    while(i<=mid) b[pos++]=a[i++];
    while(j<=end) b[pos++]=a[j++];
    for(int i=begin,j=begin;i<=end;i++,j++)
        a[i]=b[j];
}
void Sort(int begin,int end)//排序
{
    if(begin<end)
    {
        int mid=(begin+end)/2;
        Sort(begin,mid);
        Sort(mid+1,end);
        Merge(begin,mid,end);
    }
}
int main()
{
    int t,n;
    scanf("%d",&t);
    while(t--)
    {
        sum=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
            Sort(1,n);
        printf("%lld\n",sum);
    }
    return 0;
}

用树状数组求逆序数:


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1000005;
struct node
{
   int val,id;
}s[maxn];
int a[maxn],n;
int lowbit(int i)//位运算
{
    return i&(-i);
}
void update(int i)//更新
{
    while(i<=n)
    {
        a[i]++;
        i+=lowbit(i);
    }
}
int sum(int i)//求和
{
    int sum=0;
    while(i>0)
    {
        sum+=a[i];
        i-=lowbit(i);
    }
    return sum;
}
bool cmp(node x,node y)//比较函数
{
    if(x.val!=y.val)
        return x.val<y.val;
    return x.id<y.id;
}
int main()
{
    int t,i;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(i=1;i<=n;i++)
        {
            scanf("%d",&s[i].val);
            s[i].id=i;
            a[i]=0;
        }
        long long ans=0;
        sort(s+1,s+n+1,cmp);//离散化
        for(i=1;i<=n;i++)
        {
            update(s[i].id);
            ans+=i-sum(s[i].id);
        }
        printf("%lld\n",ans);
    }
    return 0;
}
时间: 2024-10-19 19:17:10

nyist oj 117 求逆序数 (归并排序&&树状数组)的相关文章

nyoj 117 求逆序数 【树状数组】+【离散化】

这道题的解法真的很好!!! 思路:建立一个结构体包含val和id, val就是输入的数,id表示输入的顺序.然后按照val从小到大排序,如果val相等,那么就按照id排序. 如果没有逆序的话,肯定id是跟i(表示拍好后的顺序)一直一样的,如果有逆序数,那么有的i和id是不一样的.所以,利用树状数组的特性,我们可以简单的算出逆序数的个数. 如果还是不明白的话举个例子.(输入4个数) 输入:9 -1 18 5 输出 3. 输入之后对应的结构体就会变成这样 val:9 -1 18 5 id:  1  

HDU 4417 类似求逆序数的树状数组

Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2250    Accepted Submission(s): 1092 Problem Description Mario is world-famous plumber. His “burly” figure and amazing jumping ability

poj 2299 Ultra-QuickSort 求逆序数,树状数组解法,详细解析

Ultra-QuickSort Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 44554   Accepted: 16195 Description In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swappin

求逆序数(树状数组+离散化)

https://www.nowcoder.com/acm/contest/77/A 步骤: 首先离散化数组,对原数组进行排序并重新编号进另一个数组中 然后按原顺序 1)一点点更新树状数组(由于树状数组的元素主要是一个数组的部分元素的和,在这里由于只关心个数,所以元素和直接统计为个数和) 2)一点点求区间和 3)把计算的区间和都相加即可 其中add()和sum()是树状数组的基本操作 原文地址:https://www.cnblogs.com/MekakuCityActor/p/8474491.ht

求逆序对 (树状数组版)

基本思想和线段树求解逆序数是一样的,前一篇<求逆序对 线段树版>也介绍过,先对输入数组离散,数组里的元素都不相同可以直接hash,存在相同的数话可以采用二分. 离散化后对于每个f[i],找到f[i]+1~ n中的个数,也就是到i这个位置,一共有多少比f[i]大的数,统计之后在将f[i]的位置上的数量加1. 这样一来统计的就是类似a[i]~n的和,可以想象成 把树状数组反过来统计,即统计的时候加lowbit,更新的时候减lowbit. 还是 以POJ 2299为例. #include <i

Day2:T4求逆序对(树状数组+归并排序)

T4: 求逆序对 A[I]为前缀和 推导 (A[J]-A[I])/(J-I)>=M A[j]-A[I]>=M(J-I) A[J]-M*J>=A[I]-M*I 设B[]=A[]-M*(); B[J]>=B[I] 也就是求逆序对: 求逆序对的方法主要有两种: 归并排序: 树状数组: 这里两种方法都学习一下: 1.之前对于树状数组的印象就只有单点修改和区间求和 一直觉得lowbit是一个神奇的东西(至今没有搞懂原理) 上网搜了一下用树状数组求逆序对的方法,发现有一个大神写的很棒....看

hdu 4911 求逆序对数+树状数组

http://acm.hdu.edu.cn/showproblem.php?pid=4911 给定一个序列,有k次机会交换相邻两个位置的数,问说最后序列的逆序对数最少为多少. 实际上每交换一次能且只能减少一个逆序对,所以问题转换成如何求逆序对数. 归并排序或者树状数组都可搞 树状数组: 先按大小排序后分别标号,然后就变成了求1~n的序列的逆序数,每个分别查询出比他小的用i减,在把他的值插入即可 #include <cstdio> #include <cstdlib> #includ

求排列的逆序数之树状数组

代码实现 #include<iostream> #include<cstdio> #include<cstdlib> using namespace std; int num[100001]; int n,a[100001]; long long count=0; void add(int x){ for(int i=x;i<=n;i+=(i&-i))num[i]++; } void query(int p){ for(int i=p; i; i -= (

逆序对 (树状数组 | | 归并排序

数组前面的一个元素 大于等于 后面的一个元素就是一个逆序对: 树状数组可以快速求前缀和,利用这一特性,可以求逆序对个数,见下: 用数组c[ i ]记录数组a[ n ]中i这一元素出现的次数 ,当a[ n ]中元素较大时可以离散化处理. 将a[ n ]从a[n -1]到a[0] 依次存到树状数组中,每存一个,对存的元素i求一次c[i]的前缀和, 这就是当前已扫描过的比i小的元素的个数,由于a[n]是倒着扫描的,所以此时比i小的元素都对应一个逆序对,统计之. #include <cstdio> #