去題面的傳送門
QAQ昨天隊內胡策的T1,成功打次了
沒做過火柴排隊的我考完試先跑過來做這道題了
首先,對於兩個序列,要使他們之間的距離最小,也就是Σ(ai+bi)^2最小,肯定是最大的和最大的對應,次大的和次大的對應,也就是說,把兩個序列排序後,各個位上一一對應。但是還要保證交換次數最少,所以不能打亂順序。
舉個栗子:
A序列:4 7 2 1
B序列:3 2 1 4
排序後:
A:1 2 4 7
B:1 2 3 4
所以我們得到1,1對應,2,2對應,4,3對應,7,4對應,所得距離最小
對於原序列,我們把每個數字排序後的序號標記在對應的原位置上
也就是:
A:3 4 2 1
B:3 2 1 4
現在問題轉化爲,如何交換B序列中的相鄰數字,使用最少的步數來使B變爲A
現在,我用C數組表示,如果要使B交換後和A相同,各個數字應該放在哪個位置上
C:1 3 4 2
(當然,C數組的統計,還要用一個類似於模擬map函數的數組實現)
也就是說交換完成後的B序列,對應的C應該是1 2 3 4的嚴格遞增1的序列
所以我們要用最少的交換次數,來使C變爲一個嚴格上升的序列,如何求最少交換次數?
求逆序對個數
證明:
對於一個嚴格遞增的序列,一定不存在逆序對。對於一個非嚴格遞增的序列,一定存在相鄰的數字爲一對逆序對,所以,我們的最小交換次數就是逆序對的個數。
我用的歸併排序,之前有歸併排序求逆序對的博客
PS:數組模擬map求C數組的時候有點暈,一定要仔細想想
代碼:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100000+10,mo=99999997;
int n,ans;
int c[maxn],fin[maxn],m[maxn];
struct hh
{
int num,x;
}a[maxn],b[maxn];
bool cmp(hh x,hh y)
{
return x.x<y.x;
}
void merge_sort(int l,int r,int mid)
{
int i=l,j=mid+1;
int k=0;
while(i<=mid&&j<=r)
{
if(c[i]>c[j])
{
m[++k]=c[j++];
ans=(ans+mid-i+1)%mo;
}
else
{
m[++k]=c[i];
i++;
}
}
while(i<=mid) m[++k]=c[i++];
while(j<=r) m[++k]=c[j++];
for(int t=l;t<=r;++t)
c[t]=m[t-l+1];
}
void done(int l,int r)
{
if(r>l)
{
int mid=(l+r)>>1;
done(l,mid);
done(mid+1,r);
merge_sort(l,r,mid);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i].x);
a[i].num=i;
}
for(int i=1;i<=n;++i)
{
scanf("%d",&b[i].x);
b[i].num=i;
}
sort(a+1,a+n+1,cmp);
sort(b+1,b+n+1,cmp);
for(int i=1;i<=n;++i) fin[i]=a[i].num;
for(int i=1;i<=n;++i) c[b[i].num]=fin[i];
done(1,n);
printf("%d",ans%mo);
return 0;
}