基准时间限制: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
思路:归并/线段树/树状数组
一,用归并思想来求解。在归并排序时两段合并时可以求出逆序数
二,用线段树或树状数组求解。首先将a[1->n]的值全部变成1再转移成线段树,那么对于区间 a[1-n],求 a[i]之前 a[1->i-1]对a[i]的逆序数,那么只要求 区间a[1->i-1]的区间和减去 比a[i]小的数之和即可,那么如何处理比a[i]小的数呢,只要将比a[i]小的数变成0就行了,那么只需要 从小到大寻找其的逆序数并修改为0即可。
Code 归并:
//1019-逆序数 归并
#include<iostream>
using namespace std;
const int MAX_N=50005;
int n,ans;
int a[MAX_N];
void merge_sort(int a[],int l,int r);
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=0;i<n;++i)
cin>>a[i];
merge_sort(a,0,n);
cout<<ans<<endl;
return 0;
}
void merge_sort(int a[],int l,int r)
{
int h=(l+r)/2;
if(r-l==1) return;
merge_sort(a,l,h);
merge_sort(a,h,r);
int i=l,j=h,k=0;
int d[r-l+1];
while(i<h&&j<r){
if(a[i]>a[j]){
ans+=h-i; d[k++]=a[j++];
}else d[k++]=a[i++];
}
while(i<h||j<r){
if(i<h) d[k++]=a[i++];
if(j<r) d[k++]=a[j++];
}
for(int i=l,k=0;i<r;++k,++i)
a[i]=d[k];
}
Code 线段树:
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAX_N=50005;
struct node{
int x;
int id;
bool operator<(const node &d)const{
return x<d.x;
}
};
int n;
LL ans;
int a[MAX_N];
node b[MAX_N];
LL sum[MAX_N<<2];
void Build(int l,int r,int t); //创建线段树
void PushUp(int t); //求和
void Update(int id,int C,int l,int r,int t); //更新节点
LL Query(int L,int R,int l,int r,int t); //查询 a[L->R]的和
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=1,x;i<=n;++i)
{
cin>>x;
b[i].id=i; b[i].x=x;
a[i]=1;
}
sort(b+1,b+n+1);
Build(1,n,1);
for(int i=1;i<=n;++i)
{
Update(b[i].id,-1,1,n,1);
ans+=Query(1,b[i].id,1,n,1); //查询 a[L->R]的和
}
cout<<ans<<endl;
return 0;
}
void PushUp(int t) //求和
{
sum[t]=sum[t<<1]+sum[t<<1|1];
}
void Build(int l,int r,int t) //创建线段树
{
if(l==r){
sum[t]=a[l];
return;
}
int h=(l+r)>>1;
Build(l,h,t<<1);
Build(h+1,r,t<<1|1);
PushUp(t);
}
void Update(int id,int C,int l,int r,int t) //更新节点
{
if(l==r){
sum[t]+=C;
return;
}
int h=(l+r)>>1;
if(id<=h) Update(id,C,l,h,t<<1);
else Update(id,C,h+1,r,t<<1|1);
PushUp(t);
}
LL Query(int L,int R,int l,int r,int t) //查询 a[L->R]的和
{
if(L<=l&&R>=r) return sum[t];
int h=(l+r)>>1;
LL ans1=0;
if(L<=h) ans1+=Query(L,R,l,h,t<<1); //左子区间与[L,R]有重叠
if(h+1<=R) ans1+=Query(L,R,h+1,r,t<<1|1); //右子区间与[L,R]有重叠,递归
return ans1;
}
Code 树状数组:
#include<iostream>
#include<algorithm>
using namespace std;
struct node{
int id;
int x;
bool operator<(const node &p)const{
return (x==p.x)?(id<p.id):(x<p.x);
}
};
const int MAX_N=50005;
int n;
node a[MAX_N];
int Tree[MAX_N];
int Lowbit(int x);
void Update(int id,int x);
int Query(int id);
int main()
{
ios::sync_with_stdio(false);
while(cin>>n){
for(int i=0;i<n;++i)
{
cin>>a[i].x;
a[i].id=i+1;
Tree[i+1]=0;
}
sort(a,a+n);
int ans=0;
for(int i=0;i<n;++i)
{
Update(a[i].id,1);
ans+=a[i].id-Query(a[i].id);
}
cout<<ans<<endl;
}
return 0;
}
int Lowbit(int x)
{
return x&(-x);
}
void Update(int id,int x)
{
while(id<=n){
Tree[id]+=x;
id+=Lowbit(id);
}
}
int Query(int id)
{
int ans=0;
while(id>0){
ans+=Tree[id];
id-=Lowbit(id);
}
return ans;
}