Minimum Inversion Number 线段树

The inversion number of a given number sequence a1, a2, …, an is the number of pairs (ai, aj) that satisfy i < j and ai > aj.

For a given sequence of numbers a1, a2, …, an, if we move the first m >= 0 numbers to the end of the seqence, we will obtain another sequence. There are totally n such sequences as the following:

a1, a2, …, an-1, an (where m = 0 - the initial seqence)
a2, a3, …, an, a1 (where m = 1)
a3, a4, …, an, a1, a2 (where m = 2)

an, a1, a2, …, an-1 (where m = n-1)

You are asked to write a program to find the minimum inversion number out of the above sequences.
Input
The input consists of a number of test cases. Each case consists of two lines: the first line contains a positive integer n (n <= 5000); the next line contains a permutation of the n integers from 0 to n-1.
Output
For each case, output the minimum inversion number on a single line.
Sample Input
10
1 3 6 9 0 8 5 7 4 2
Sample Output
16

题目大意就是求给出的数字序列的最小逆序数,就是说可以对这个数字序列的顺序做左右移动,找左右排序中的最小逆序数,用暴力的方法会超时,所以采用线段树进行化简。

代码中有这么几点需要注意,由于逆序数的特性,每次调整顺序都会满足:
sum = sum + (n - 1 - a[i]) - a[i];
利用这个,我们就只需要求初始序列的逆序数,剩下排列的逆序数只需要利用这个规律就可以求出来了。此外,求初始序列的逆序数时,由于输入是一个一个输入的,我们只需要利用这一点,找当前数字前面输入的比当前数字大的数,利用线段树的特性,就不需要进行复杂的比较了。在具体实现过程中可以用移位元算来替代乘法运算以减小时间复杂度。
AC代码

#include<iostream>
#include<stdio.h>
using namespace std;
struct Node{
	int l,r,num;
};
struct Node nodes[5005];
int nnum[5005];
void build(int n,int x,int y)
{
	nodes[n].l=x;
	nodes[n].r=y;
	nodes[n].num=0;
	if(x==y) return ;
	int mid=(x+y)>>1;
	build(n<<1,x,mid);
	build(n<<1|1,mid+1,y);
}
void modify(int n,int x)
{
	int l=nodes[n].l;
	int r=nodes[n].r;
	int mid=(l+r)>>1;
	if(x==l&&x==r)
	{
		nodes[n].num=1;
		return;
	}
	if(x<=mid) modify(n<<1,x);
	else modify(n<<1|1,x);
	nodes[n].num=nodes[n<<1].num+nodes[n<<1|1].num;
}
int query(int n,int x,int y)
{
	int l=nodes[n].l;
	int r=nodes[n].r;
	int mid=(l+r)>>1;
	int ans=0;
	if(x==l&&y==r)
		return nodes[n].num;
	if(x<=mid) ans+=query(n<<1,x,min(mid,y));
	if(y>mid) ans+=query(n<<1|1,max(mid+1,x),y);
	return ans;
}
int main()
{
	int n,sum,ans;
	while(scanf("%d",&n)!=EOF)
	{
		sum=0;
		build(1,0,n);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&nnum[i]);
			modify(1,nnum[i]);
			sum+=query(1,nnum[i]+1,n);
		}
		ans=sum;
		for(int i=1;i<n;i++)
		{
			sum=sum+(n-1-nnum[i])-nnum[i];
			if(sum<ans)
				ans=sum;
		}
		cout<<ans<<endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43849505/article/details/86989363