原题链接洛谷提高组-火柴排队
分析
这题我是用离散化,数组映射(数据处理办法),归并排序优化逆序对 写的。
关于(ai-bi)的平方 求和可以理解为 a组火柴和b组火柴 相对位置相等时 这个和最小
竟然有相对位置了就逃不了离散化数组了
对于求相邻交换次数,是不是很像冒泡排序。这时候就要引入逆序对了。
可是这个逆序对的参照是1,2,3,4,5的升序来判断的,我们a转成b的相对位置可不是
1,2,3,4,5来排的所以我们把b数组映射成1,2,3,4,5,然后把a数组也按照相同的映射方法改写一下,再用逆序对写。(不用太在意重复的数据,因为仅大于才交换)
逆序对用常规的暴力方法 时间杂复度过大,所以这里我用了归并排序来优化
总结
1,数据离散化
2,数组映射
3,逆序对
代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100009;
struct data//结构体离散化
{
ll val;
ll id;
}olda[N],oldb[N];
ll newa[N],copyy[N],a[N],b[N];
//为了好理解数组开的有点多,写的时候可以省一些
bool cmp(struct data x,struct data y)
{
return x.val<y.val;
}
ll cnt=0,cup[N];
void merge(ll left,ll right)
{
if(left>=right)
{
return;
}
ll mid=(left+right)/2;
merge(left,mid);
merge(mid+1,right);
ll i=left,j=mid+1,k=left;
while(i<=mid && j<=right)
{
if(newa[i]<=newa[j])
{
cup[k++]=newa[i++];
}else
{
cup[k++]=newa[j++];
cnt+=mid-i+1;//与归并排序相比就加了这两条
cnt=cnt%99999997;
}
}
while(i<=mid)
{
cup[k++]=newa[i++];
}
while(j<=right)
{
cup[k++]=newa[j++];
}
for (int v=left;v<=right;v++)
{
newa[v]=cup[v];
}
}
int main()
{
int n;
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&olda[i].val);
olda[i].id=i;
}
for(int i=1;i<=n;i++)
{
scanf("%lld",&oldb[i].val);
oldb[i].id=i;
}
sort(olda+1,olda+1+n,cmp);
sort(oldb+1,oldb+1+n,cmp);
for(ll i=1;i<=n;i++)
{
a[olda[i].id]=i;
//不用特判相同时id相等,后面逆序对的处理方向可以避免相等时相交换
//直接先出现的小
}
for(ll i=1;i<=n;i++)
{
b[oldb[i].id]=i;
}
//假设以b为参照物
//求逆序对时暴力会超时所以可以进行优化,我用归并优化
//将第2盒火柴映射为升序,相当于把第二盒当成id为1,2,3,4,5然后把第一盒按id去改
for(ll i = 1; i <= n; i ++) copyy[b[i]] = i;//copyy作为按b转化的表,想出这个方法的人真是天才!!
//将第1盒火柴当对应的元素进行映射为第一种表示
for(ll i = 1; i <= n; i ++) newa[i] = copyy[a[i]];
merge(1,n);
printf("%lld\n",cnt%99999997);
return 0;
}