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;
}