【NOIP2013】火柴排队
如果没有这道题的话我连逆序对是啥都不知道QAQ
原题:
涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:
,其中 ai 表示第一列火柴中第 i 个火柴的高度,bi 表示第二列火柴中第 i 个火柴的高度。
每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997 取模的结果。
1 ≤ n ≤ 100,000,0 ≤火柴高度≤ 2^31-1。
首先根据距离定义式可以看粗来,让距离最小,就是让第一列中的第k小的火柴和第二列中的第k小的火柴对齐,而且只能相邻的两个交换,这就是个冒泡
冒泡排序中,如果要把序列排成升序,呢么如果存在i<j && a[i]>a[j],呢么a[i]和a[j]一定要交换,因为a[i]要往右走,a[j]要往左走
定义id为a排序前的位置,数组c为a[i].id对应的b[i].id,呢么就是求c中的逆序对个数,即让两列中第k小的火柴对齐需要的最小交换次数
归并求逆序对即可
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 int read(){int z=0,mark=1; char ch=getchar(); 8 while(ch<'0'||ch>'9'){if(ch=='-')mark=-1; ch=getchar();} 9 while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0'; ch=getchar();} 10 return z*mark; 11 } 12 int mo=99999997; 13 int n; 14 struct cdd{int num,id;}a[110000],b[110000]; 15 int c[110000]; 16 int d[110000],dtou=0; 17 int bowl=0; 18 bool compare(cdd x,cdd y){return x.num<y.num;} 19 void bing(int _left,int _right){ 20 int mid=(_left+_right)/2; dtou=_left-1; 21 int i=_left,j=mid+1; 22 while(i<=mid && j<=_right){ 23 if(c[j]<c[i]){ d[++dtou]=c[j++]; bowl=(bowl+mid-i+1)%mo;} 24 else d[++dtou]=c[i++]; 25 } 26 while(i<=mid) d[++dtou]=c[i++];//序列是单调的,所以后面的肯定也都大于前面的 27 while(j<=_right) d[++dtou]=c[j++]; 28 for(int i=_left;i<=_right;i++) 29 c[i]=d[i]; 30 } 31 void gui(int _left,int _right){ 32 if(_left<_right){ 33 int mid=(_left+_right)/2; 34 gui(_left,mid),gui(mid+1,_right); 35 bing(_left,_right); 36 } 37 } 38 int main(){//freopen("ddd.in","r",stdin); 39 cin>>n; 40 for(int i=1;i<=n;i++) a[i].num=read(),a[i].id=i; 41 for(int i=1;i<=n;i++) b[i].num=read(),b[i].id=i; 42 sort(a+1,a+n+1,compare),sort(b+1,b+n+1,compare); 43 for(int i=1;i<=n;i++) c[a[i].id]=b[i].id; 44 gui(1,n); 45 /*for(int i=1;i<=n;i++) cout<<c[i]<<" "; 46 cout<<endl;*/ 47 cout<<bowl<<endl; 48 return 0; 49 }