51Nod 求逆序数 树状数组 + 离散化

基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题
收藏
关注
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。
如2 4 3 1中,2 1,4 3,4 1,3 1是逆序,逆序数是4。给出一个整数序列,求该序列的逆序数。
Input
第1行:N,N为序列的长度(n <= 50000)
第2 - N + 1行:序列中的元素(0 <= A[i] <= 10^9)
Output
输出逆序数
Input示例
4
2
4
3
1
Output示例
4

方法1:归并排序


#include<stdio.h>  

#define maxsize 50010
long long int count = 0;
int a[maxsize],temp[maxsize];  
void merge(int s,int m,int e)  
{  
    int p1 = s,p2 = m+1,p = 0;
    while(p1 <= m && p2 <= e)  
        {  
        if(a[p1]<=a[p2])  
            temp[p++]=a[p1++];  
        else  
        {  
            temp[p++]=a[p2++];  
            count += m-p1+1; 
        }  
    }  
    while(p1<=m)
        temp[p++]=a[p1++];  
    while(p2<=e)  
        temp[p++]=a[p2++];  
    int i;  
    for(i = 0;i < p; ++i)
    	a[s+i] = temp[i];  
}  
  
  
void mergesort(int l,int r)  
{  
    if(l<r)  
    {  
       	int m = (l+r)/2;  
        mergesort(l,m);  
        mergesort(m+1,r);  
        merge(l,m,r);
    }  
}  
  
  
int main()  
{  
        int n,i;  
        scanf("%d",&n);  
        for(i=0;i<n;i++)  
            scanf("%d",&a[i]);  
        mergesort(0,n-1);
        printf("%lld\n",count);  
        return 0;  
} 
方法二:树状数组 + 离散化

离散化参考: 离散化
树状数组: 树状数组讲解

树状数组求逆序原理:点击打开链接

#include<bits/stdc++.h>
using namespace std;
const int MAXN  = 50005;
int C[MAXN];
int a[MAXN];
int n;
struct Node{
	int d,ord;//数据与 位置 
	bool operator<(const Node o)const{
		return d < o.d;
	}
}p[MAXN];
int lowBit(int i){
	return i&(-i);
}
int add(int i,int x){
	while(i <= n){
		C[i] += x;
		i += lowBit(i);
	}
}
int getSum(int i){
	int sum = 0;
	while(i){
		sum += C[i];
		i -= lowBit(i);
	}
	return sum;
}

int main()
	{
		scanf("%d",&n);
		memset(C,0,sizeof(C));
		for(int i = 1; i <= n; ++i){//从1开始
			scanf("%d",&p[i].d);
			p[i].ord = i;
		}
		sort(p+1,p+n+1);//按照数据大小 从小到大排序
		//数据被离散化为从1开始的数 
		for(int i = 1; i <= n; ++i){
			a[p[i].ord] = i;
		} 
		int ans = 0;
		for(int i = 1; i <= n; ++i){
			add(a[i],1);//a[i]表示新的数据
			ans += i - getSum(a[i]); //这里 i可以用getSum(n)代替 i表示当前共有多少数 减去比a[i]小的数
		}
		printf("%d\n",ans);
		
		return 0;
	}
在数据范围比较小的情况下,可以不用离散化
但是需要注意,数据不能有 0 值,若有0可以对每个数据加一的方式或者进行离散化

原因:0&0 == 0 此时无法实现i的更新 会进入死循环(add中的while)

#include<bits/stdc++.h>
using namespace std;
const int MAXN  = 50000;
int C[MAXN];
int n;
int lowBit(int i){
	return i&(-i);
}
int add(int i, int x){
	while(i <= n){
		C[i] += x;
		i += lowBit(i);	
	}
}
int getSum(int i){
	int sum = 0;
	while(i > 0){
		sum += C[i];
		i -= lowBit(i);
	}
	return sum;
}
int main()
	{
		memset(C,0,sizeof(C));
		scanf("%d",&n);
		int x;
		int ans = 0;
		for(int i = 1; i <= n; ++i){
			scanf("%d",&x);
			add(x,1);
			ans += i - getSum(x);
		}
		printf("%d\n",ans);
//		for(int i = 1; i <= n; ++i){
//			printf("%d ",C[i]);
//		}
				//cout << getSum(5)-getSum(3);
		
		return 0;
	}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值