NOIP2013提高组 T2 火柴排队
一开始看也想不到这居然要用到逆序对,归并排序。
先来看看题目:
涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为: ∑(ai-bi)^2
其中 ai 表示第一列火柴中第 i 个火柴的高度,bi 表示第二列火柴中第 i 个火柴的高度。
每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997 取模的结果。
样例输入:
4 1 3 4 2 1 7 2 4
样例输出:
2
根据样例,设第二行的数为a{1,3,4,2},第三行的数为b{1,7,2,4}。对于题目要求通过交换使得两列火柴之间的的距离最小,满足这个要求只能使a中高度最小的火柴和b中高度最小的火柴搭配,第二小的和第二小的搭配。为了方便处理和后续的操作,把a和b进行离散化。离散化后,a{1,3,4,2},b{1,4,2,3}(离散化详解在*1)。遍历数组a,查找a[i]在数组b中所对应的位置(详细处理在*2)(需要用二分查找*3),存入新数组R,处理后为R{1,4,2,3},R[i]即为a[i]将要移到的位置,最小交换的次数正是R的逆序对数量。a中的3要移到R中3的位置,需要向右交换两次。相同的,可以这样认为,R中的3要向左移到a中的3的位置,则需要两次交换。R中3(即R[4])的逆序对数量为2,整个R的逆序对数量也为2。所求最少交换次数就是R的逆序对数量。使用归并排序。
时间复杂度(大概):O(7*(n log n)) 离散化:4*(n log n) ( 4次快排) 预处理二分查找+二分查找:2*(n log n) 归并排序:(n log n) 绝对不会超时qwq
*1:离散化前:a{1,3,4,2},b{1,7,2,4},给每个数组的每一个数标上序号,按照数组内容带上序号一起排序,重新编号,再根据之前标上的序号排回来恢复原状。
原始状态:
a | 1 | 3 | 4 | 2 |
序号 | 1 | 2 | 3 | 4 |
b | 1 | 7 | 2 | 4 |
序号 | 1 | 2 | 3 | 4 |
按照数组内容排序:
a | 1 | 2 | 3 | 4 |
序号 | 1 | 4 | 2 | 3 |
b | 1 | 2 | 4 | 7 |
序号 | 1 | 3 | 4 | 2 |
重新编号:
a | 1 | 2 | 3 | 4 |
序号 | 1 | 4 | 2 | 3 |
b | 1 | 2 | 3 | 4 |
序号 | 1 | 3 | 4 | 2 |
按照序号重新排序恢复:
a | 1 | 3 | 4 | 2 |
序号 | 1 | 2 | 3 | 4 |
b | 1 | 4 | 2 | 3 |
序号 | 1 | 2 | 3 | 4 |
完成,a离散化后和原来一样是因为样例特殊,参考b的离散化过程就好了。
*2:a[i]在数组b中所对应的位置。首先列出数组。
a | 1 | 3 | 4 | 2 |
b | 1 | 4 | 2 | 3 |
序号 | 1 | 2 | 3 | 4 |
R |
i=1的情况:a[i]为1,b中的1在b[1]中,b[1]对应的序号为1,所以R[i]为1,i=1,所以R[1]填上1。
i=2的情况:a[i]=3,b[4]=3,所以b中的3在b[4],对应序号为4。R[2]填上4
i=3:a[i]=4,b[2]=4,所以R[i]=2,R[3]填上2
i=4:a[i]=2,b[3]=2,所以R[4]=3
最终得R为:
R | 1 | 4 | 2 | 3 |
*3:如果查找a[i]在数组b中所对应的位置使用两重循环,则查找的时间复杂度为O(n^2),数据范围为n<=100000,光是查找就会超时,所以需要用二分排序。
贴上代码详细参考:
1 type 2 arr=array[0..100000] of longint; 3 var 4 a1,b1,a,r:arr; 5 n,i,j:longint; 6 left,right,mid:longint; 7 ans:qword; 8 procedure qsort(var a,b:arr); //快排a数组,捆绑b数组 9 procedure sort(l,r: longint); 10 var 11 i,j,x,y: longint; 12 begin 13 i:=l; 14 j:=r; 15 x:=a[(l+r) div 2]; 16 repeat 17 while a[i]<x do 18 inc(i); 19 while x<a[j] do 20 dec(j); 21 if not(i>j) then 22 begin 23 y:=a[i]; 24 a[i]:=a[j]; 25 a[j]:=y; 26 y:=b[i]; 27 b[i]:=b[j]; 28 b[j]:=y; 29 inc(i); 30 dec(j); 31 end; 32 until i>j; 33 if l<j then 34 sort(l,j); 35 if i<r then 36 sort(i,r); 37 end; 38 begin 39 sort(1,n); 40 end; 41 procedure mergesort(s,t:longint); //归并排序用来统计逆序对 42 var 43 mid,i,j,k:longint; 44 begin 45 if s=t then exit; 46 mid:=(s+t) div 2; 47 mergesort(s,mid); 48 mergesort(mid+1,t); 49 i:=s; 50 j:=mid+1; 51 k:=s; 52 while (i<=mid) and (j<=t) do 53 if a[i]<=a[j] then 54 begin 55 r[k]:=a[i]; 56 inc(i); 57 inc(k); 58 end 59 else 60 begin 61 r[k]:=a[j]; 62 inc(j); 63 inc(k); 64 ans:=ans+(mid-i+1); //逆序对 65 end; 66 while i<=mid do 67 begin 68 r[k]:=a[i]; 69 inc(i); 70 inc(k); 71 end; 72 while j<=t do 73 begin 74 r[k]:=a[j]; 75 inc(j); 76 inc(k); 77 end; 78 for i:=s to t do 79 a[i]:=r[i]; 80 end; 81 begin 82 assign(input,'match.in'); 83 assign(output,'match.out'); 84 reset(input); 85 rewrite(output); 86 readln(n); 87 for i:=1 to n do 88 begin 89 read(a1[i]); //读入a数组 90 r[i]:=i; //编号 91 end; 92 qsort(a1,r); //按照数组内容排序 93 for i:=1 to n do a1[i]:=i; //重新编号 {离散化} 94 qsort(r,a1); //按照编号排序复原 95 readln; 96 for i:=1 to n do 97 begin 98 read(b1[i]); //读入b数组 99 r[i]:=i; //编号 100 end; 101 qsort(b1,r); //按照数组内容排序 102 for i:=1 to n do b1[i]:=i; //重新编号 {离散化} 103 qsort(r,b1); //按照编号排序复原 104 105 {for i:=1 to n do //处理a[i]在数组b中所对应的位置,使用两重循环,n到达1000时就会超时,所以舍弃 106 for j:=1 to n do 107 if b1[i]=a1[j] then 108 begin 109 a[i]:=j; 110 break; 111 end; } 112 113 for i:=1 to n do r[i]:=i; 114 qsort(a1,r); //按照内容排序 115 for i:=1 to n do //遍历每个b[i]查找在a中的位置(这里a和b交换了,所以是查找b[i]在a中的位置) 116 begin 117 left:=1; 118 right:=n; 119 mid:=(left+right) div 2; 120 while (a1[mid]<>b1[i]) and (left<=right) do //二分查找,时间复杂度O(n log n) 121 begin 122 if a1[mid]>b1[i] then right:=mid-1 123 else left:=mid+1; 124 mid:=(left+right) div 2; 125 end; 126 a[i]:=r[mid]; //此处的a数组即为上面所讲的R数组 127 end; 128 ans:=0; 129 fillchar(r,sizeof(r),0); 130 mergesort(1,n); //归并排序统计逆序对 131 writeln(ans mod 99999997); //依照题意对99999997取模 132 close(input); 133 close(output); 134 end.