JOJ2306 Marriage(归并排序求逆序数)

2306: Marriage

 

Result TIME Limit MEMORY Limit Run Times AC Times JUDGE
3s 8192K 424 60 Standard

Now, a lot of persons holding their marriages together are in fashion. One day, a lot of people hold their marriages together. They are all happy, so they want to play a game. They stand in two lines, one faces one. The men are in one line, the women are in another. They stand arbitrarily. Then, there will be some red lines to link each couple. So can you calculate how many pairs of the red lines are overlaped.

Input

There are several cases in the input, each case begin with a postive integer N(N<=300000), which means there are N couples, 0 means the end of the file. The following 2 lines each consists of N integers. Each integer represents a couple. The first line are men, the second are women.

Output

For each case output how many pairs of lines are overlaped.

Sample Input

3
1 2 3
3 2 1
3
1 2 3
1 2 3
0

Sample Output

3
0

 

Problem Source: Parhelic

 

 

这题搁置了有一段时间了,一直没有找到好的办法,当时网上就有人说是归并排序求逆序数,今天数据结构课老师刚讲了归并排序,我就来试试看,结果真的AC掉了.排名还不错呢。

废话到此.

 

首先说说这道题的大体思路:

实际上我们可以以第一排各个数字的出现顺序为基准,来对第二排的序列求逆序数,求得的逆序数也就是the number of lines are overlaped.(不知看懂没有,可不是直接对第二排来求逆序数,而是以第一排的序列顺序为基准来求)。

我们不妨假定第一排的出现的数字代表的是人的姓名(这个国家很奇怪姓名都用数字代表)那么按出现的顺序我们依次将各个姓名标记上序号

(1,2,3,4...n),也就是一对夫妻中妻子在其所在排列的顺序。

那么在处理第二排的时候我们将各个按顺序读入丈夫的"名字"(假设每一对夫妻有一个顺序自己的唯一名字),当然读入的顺序也就是丈夫所在自己排的位置,我们在根据各个丈夫的名字,来反着找到其妻子所在排中的位置。这样子我们就把第二排的序列转化成了一个以第一排序列为基准的待排序序列,然后我们利用并归排序来求解这个待排序序列的逆序数,也就是本题要求的答案了。

 

还要解释一下为什么求逆序数就是结果呢?

因为我们读入丈夫的顺序实际就是各个丈夫的排列顺序,先读的就在前面,根据我们上边的操作,每个丈夫上的数字都对应着自己妻子的位置,我们假设任意两个丈夫h1,h2(h1在h2前面),那么如果h1上对应其妻子的位置w1比h2上对应其妻子的位置w2大,那么就意味着有一个交叉,转化到我们构造出来的序列上也就是说有一个你逆序数,以此类推则整个序列的逆序数也就是一共交叉的个数了,聪明的你,明白了吗?

 

如果不太理解,就多分析几次吧,这题的数据给的不给力。

 

接下来再说说归并排序怎么求逆序数(归并排序算法自己查资料吧,这个老师会讲吧)。

 

只需讨论归并排序算法里最底层的操作。

假设我们要把两组已经有序的子序列Arr1,Arr2给合并到一起。

不妨假设其中Arr1={1,5,6,9},Arr2={2,4,7}.(在整个数组中Arr1的所有元素都是在Arr2之前的)

比如Arr1的比较指针指向2,而Arr2的比较指针指向1。

此时Arr1[2]>Arr2[1]。

那么就意味着对于Arr1中的元素从位置2到最后都比Arr2[1]要大,又因为Arr1中的全部元素都在Arr2之前.

所以就意味着(5,2)(6,2)(9,2)都是一个逆序对,所以这一步里就得到了三个逆序数(这可远远不是全部)。

在具体程序中如何计算可以参照我们程序。

 

最后要记住,用long long(double也可)来保存逆序数,我就是用int WA了好几次。

膜拜0.00s过的仁兄!

Code:

 

 <textarea cols="50" rows="15" name="code" class="cpp">#include&lt;stdio.h&gt; int n; int a[300001]; int b[300001]; int x[300001]; int length; long long res; void Merge(int arr[],int t,int m,int n,int x[]) { int i=t; int j=m+1; int k=t; while(i&lt;=m&amp;&amp;j&lt;=n) { if(arr[i]&lt;=arr[j]) { x[k++]=arr[i++]; } else { res+=(m-i+1); x[k++]=arr[j++]; } } while(i&lt;=m) { x[k++]=arr[i++]; } while(j&lt;=n) { x[k++]=arr[j++]; } } void MPass(int arr[],int n,int length,int x[]) { int i=1; while(i&lt;=n-2*length+1) { Merge(arr,i,i+length-1,i+2*length-1,x); i+=2*length; } if(i+length-1&lt;n) { Merge(arr,i,i+length-1,n,x); } else { int j; for(j=i;j&lt;=n;j++) { x[j]=arr[j]; } } } void Msort(int arr[],int n) { length=1; while(length&lt;n) { MPass(arr,n,length,x); length*=2; MPass(x,n,length,arr); length*=2; } } int main() { while(scanf("%d",&amp;n)&amp;&amp;n) { int i; int temp; for(i=1;i&lt;=n;i++) { scanf("%d",&amp;temp); a[temp]=i; } for(i=1;i&lt;=n;i++) { scanf("%d",&amp;temp); b[i]=a[temp]; } res=0; Msort(b,n); printf("%lld/n",res); } } </textarea>

你可能感兴趣的:(JOJ2306 Marriage(归并排序求逆序数))