【NOIP2013】火柴排队

时间:2021-10-09 14:46:54

如果没有这道题的话我连逆序对是啥都不知道QAQ

原题:

涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:

【NOIP2013】火柴排队

,其中 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小的火柴对齐需要的最小交换次数

归并求逆序对即可

代码:

 #include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int read(){int z=,mark=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-')mark=-; ch=getchar();}
while(ch>=''&&ch<=''){z=(z<<)+(z<<)+ch-''; ch=getchar();}
return z*mark;
}
int mo=;
int n;
struct cdd{int num,id;}a[],b[];
int c[];
int d[],dtou=;
int bowl=;
bool compare(cdd x,cdd y){return x.num<y.num;}
void bing(int _left,int _right){
int mid=(_left+_right)/; dtou=_left-;
int i=_left,j=mid+;
while(i<=mid && j<=_right){
if(c[j]<c[i]){ d[++dtou]=c[j++]; bowl=(bowl+mid-i+)%mo;}
else d[++dtou]=c[i++];
}
while(i<=mid) d[++dtou]=c[i++];//序列是单调的,所以后面的肯定也都大于前面的
while(j<=_right) d[++dtou]=c[j++];
for(int i=_left;i<=_right;i++)
c[i]=d[i];
}
void gui(int _left,int _right){
if(_left<_right){
int mid=(_left+_right)/;
gui(_left,mid),gui(mid+,_right);
bing(_left,_right);
}
}
int main(){//freopen("ddd.in","r",stdin);
cin>>n;
for(int i=;i<=n;i++) a[i].num=read(),a[i].id=i;
for(int i=;i<=n;i++) b[i].num=read(),b[i].id=i;
sort(a+,a+n+,compare),sort(b+,b+n+,compare);
for(int i=;i<=n;i++) c[a[i].id]=b[i].id;
gui(,n);
/*for(int i=1;i<=n;i++) cout<<c[i]<<" ";
cout<<endl;*/
cout<<bowl<<endl;
return ;
}