POJ 2299 求逆序数 归并排序||离散化&树状数组

http://poj.org/problem?id=2299

In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swapping two adjacent sequence elements until the sequence is sorted in ascending order. For the input sequence

9 1 0 5 4 ,


Ultra-QuickSort produces the output

0 1 4 5 9 .


Your task is to determine how many swap operations Ultra-QuickSort needs to perform in order to sort a given input sequence.

Input

The input contains several test cases. Every test case begins with a line that contains a single integer n < 500,000 -- the length of the input sequence. Each of the the following n lines contains a single integer 0 ≤ a[i] ≤ 999,999,999, the i-th input sequence element. Input is terminated by a sequence of length n = 0. This sequence must not be processed.

Output

For every input sequence, your program prints a single line containing an integer number op, the minimum number of swap operations necessary to sort the given input sequence.

Sample Input

5
9
1
0
5
4
3
1
2
3
0

Sample Output

6
0

题目大意:求逆序数,即i<j但ai>aj的数量。

思路:归并排序或者树状数组+离散化。归并排序的思路很简单,就在L[i]>R[j]的时候加了一个语句,即cnt+=temp1-i。temp1表示左半边区间的元素个数,很明显,此时R[j]<L[i],那么左边区间还有i-temp1个数均大于R[j],因此对答案贡献了temp1-i个逆序对。说一下树状数组的思路,首先做离散化把值都转化成1-n,那么对于i,若其前面有k个数小于a[i],那么可以贡献i-1-k个逆序对。(不明白的仔细思考一下 i之前最多有1、2……i-1 共i-1个数 若k个数小于a[i] 那么还剩下i-1-k个数大于a[i]) 根据这个思路,我们对离散化后的序列从0到n开始遍历,每个a[i]贡献i-sum(a[i]),(因为i从0开始了 就相当于i-1)然后做add(a[i],1)即可。

归并排序:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;

const int maxn=500005;

int a[maxn];
int L[maxn];
int R[maxn];

ll Merge(int l,int mid,int r)
{
	int temp1=mid-l;
	int temp2=r-mid;
	for(int i=0;i<temp1;i++)
		L[i]=a[l+i];
	for(int i=0;i<temp2;i++)
		R[i]=a[mid+i];
	int i=0,j=0,k=l;
	ll cnt=0;
	while(i<temp1&&j<temp2)
	{
		if(L[i]>R[j])
		{
			a[k++]=R[j++];
			cnt+=temp1-i;
		}
		else
			a[k++]=L[i++];
	}
	while(i!=temp1)
		a[k++]=L[i++];
	while(j!=temp2)
		a[k++]=R[j++];
	return cnt;
}

ll MergeSort(int l,int r)
{
	if(l+1<r)//最少要有两个元素
	{
		int mid=(l+r)>>1;
		ll sum1=MergeSort(l,mid);
		ll sum2=MergeSort(mid,r);
		ll sum3=Merge(l,mid,r);
		return sum1+sum2+sum3;
	}
	return 0;
}

int main()
{
	int n;
	while(~scanf("%d",&n)&&n)
	{
		for(int i=0;i<n;i++)
			scanf("%d",&a[i]);
		printf("%lld\n",MergeSort(0,n));
	}
	return 0;
}

离散化+树状数组:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;

const int maxn=500005;
int a[maxn];
int b[maxn];
int tree[maxn];
int n;

inline int lowbit(int x)
{
	return x&(-x);
}

void add(int i)
{
	for(;i<=n;i+=lowbit(i))
		tree[i]+=1;
}

int sum(int i)
{
	int s=0;
	for(;i;i-=lowbit(i))
		s+=tree[i];
	return s;
}

int main()
{
	while(~scanf("%d",&n)&&n)
	{
		memset(tree,0,sizeof(tree));
		for(int i=0;i<n;i++)
		{
			scanf("%d",&a[i]);
			b[i]=a[i];
		}
		sort(b,b+n);
		for(int i=0;i<n;i++)
			a[i]=upper_bound(b,b+n,a[i])-b;
		ll cnt=0;
		for(int i=0;i<n;i++)
		{
			cnt+=i-sum(a[i]);
			add(a[i]);
		}
		printf("%lld\n",cnt);
	}
	return 0;
}

上面那种树状数组统计的是第i个数前面有多少个小于a[i]的数,然后得到大于a[i]的数的个数,即贡献的逆序数。其实还有另外一种思路,因为数据范围是[1,n]的,因此若我们设a[i]=j,那么整个序列中最多有j-1个数小于a[i],而我们又知道了在a[i]之前且小于a[i]的数的个数,那么剩余的小于a[i]的数的个数为a[i]-1-sum(a[i])且其均在a[i]后方,因此贡献的逆序数为a[i]-1-sum(a[i]),由此我们得到另外一种写法:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;

const int maxn=500005;
int a[maxn];
int b[maxn];
int tree[maxn];
int n;

inline int lowbit(int x)
{
	return x&(-x);
}

void add(int i)
{
	for(;i<=n;i+=lowbit(i))
		tree[i]+=1;
}

int sum(int i)
{
	int s=0;
	for(;i;i-=lowbit(i))
		s+=tree[i];
	return s;
}

int main()
{
	while(~scanf("%d",&n)&&n)
	{
		memset(tree,0,sizeof(tree));
		for(int i=0;i<n;i++)
		{
			scanf("%d",&a[i]);
			b[i]=a[i];
		}
		sort(b,b+n);
		for(int i=0;i<n;i++)
			a[i]=upper_bound(b,b+n,a[i])-b;
		ll cnt=0;
		for(int i=0;i<n;i++)
		{
			cnt+=a[i]-1-sum(a[i]);
			add(a[i]);
		}
		printf("%lld\n",cnt);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xiji333/article/details/88367363
今日推荐