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.
时间: 2024-10-03 14:01:54

NOIP2013提高组 T2 火柴排队的相关文章

NOIP2013提高组DAY1T2火柴排队 vijos1842

火柴排队 描述 涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度.现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:∑i=1n(ai−bi)2,其中 ai 表示第一列火柴中第 i 个火柴的高度,bi 表示第二列火柴中第 i 个火柴的高度. 每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小.请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997 取模的结果. 格式 输入格

【NOIP2013提高组】火柴排队

https://www.luogu.org/problem/show?pid=1966 Σ(ai-bi)2=Σai2+Σbi2-2Σai*bi,要使Σ(ai-bi)2最小,则需2Σai*bi最大. 由排序不等式可知两列数字里第一大与第一大对应,第二大与第二大对应,--,第k大与第k大对应,--,第n大与第n大对应时,Σai*bi最大. 故先将第一列每个数字映射到第二列排名相同的数字,再求需要交换的次数,也就是逆序对的个数. #include <algorithm> #include <i

NOIP 提高组2013 火柴排队 (Vijos P1842)

描述 涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度.现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:∑ i=1 n (a i ?b i ) 2  ,其中 a i   表示第一列火柴中第 i 个火柴的高度,b i   表示第二列火柴中第 i 个火柴的高度. 每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小.请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997 取模

【NOIP】提高组2013 火柴排队

[算法]求逆序对(树状数组)(也可以用归并排序等) [题解] 学自:http://blog.csdn.net/greatwjj/article/details/16808243 https://vijos.org/p/1842/solution(从下往上第三个回答) 因此要使结果最小,只要最大化aibi(i=1...n),即使小的数对应小的数,大的数对应大的数. 1.对于数列a,b先离散化,将他们值替换为他们在所在数列的排名. 2.令数列c记录a中每个数应该在b中的位置(a的目标位置). 3.对

noip提高组2013 火柴排队

题目链接:https://www.luogu.org/problem/show?pid=1966 这个题啊,naive(虽然我不会证明). 举了个特例,得出结论:对于两列数,一定是最大与最大的相对,最小的与最小的相对. 然后就以离散化一下,然后随便用个树状数组求个逆序对就好了. 一开始我还分别写了两个逆序对,想求个差的绝对值,发现样例过不了之后...... #include<cstdio>#include<algorithm>using namespace std;const in

noip2013 提高组

T1 转圈游戏 题目传送门 果不其然 第一题还是模拟题 一波快速幂解决问题 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int read(){ int ans=0,f=1,c=getchar(); while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();} while(c>='0'&&c&

【解题】noip2013提高组(day1+day2)

这套题,勾起了我无数美好的回忆←意思是该好好复习了... [day1] 一.转圈游戏 首先,第一题,在处理k的时候应该用快速幂算法. 大概就是下面这样,要注意的是:1.二分时要判断有无余数.2.先设数,在进行乘积运算,不然会递归两次=.= 1 int pow(int a,int pos) 2 { 3 if(pos==1) return a%t; 4 int temp=pow(a,pos/2); 5 if(pos%2==1) return (temp*temp*a)%t; 6 return (te

火柴排队——noip2013——提高组

作为D1T2的这题,做法的确挺巧妙的. 首先,我们发现: 对于a1<a2,   b1>b2 则  (a1-b1)^2+(a2-b2)^2>(a1-b2)^2+(a2-b1)^2 自己拆开推一下就知道了.. 然后我们对数据进行离散化.把b数组的元素映射到a里. 由于我们需要求,使映射完的结果不下降,需要调换多少次. 求这个,也就可以转换成求逆序对. 然后,就大功告成了. #include<iostream> #include<cstdio> #include<

洛谷 P1098 字符串的展开(NOIp2007提高组T2)

题目描述 在初赛普及组的"阅读程序写结果"的问题中,我们曾给出一个字符串展开的例子:如果在输入的字符串中,含有类似于"d-h"或者"4-8"的字串,我们就把它当作一种简写,输出时,用连续递增的字母或数字串替代其中的减号,即,将上面两个子串分别输出为"defgh"和"45678".在本题中,我们通过增加一些参数的设置,使字符串的展开更为灵活.具体约定如下: (1) 遇到下面的情况需要做字符串的展开:在输入的字