一般来说我是很少写noip题解的:
但是这道题困扰了我很久(因为我太弱啦,事实上这只是一道傻逼题)
题目大意:我就不说了,汉语大家都能看懂
题解:这道题的实质是用归并排序求逆序对,这是为什么呢?
首先,我们很容易猜想将a、b两个数组中的数从大到小一一对应才是最优解,
但是因为两组一块挪和只挪一组是一样的,所以最少数我们是用一组相对于另一组做,求出b对于a来说,原来b[i]的火柴应该挪到才c[i]号位
于是得到c[i],接下来求挪多少次可以使c恢复有序,也就是1,2,3...n,这个的最少挪动次数实际上就是逆序对
{不过,会不会有人和我这个超弱的人一样不知道怎么用归并求逆序对呢?
事实上这很好理解:
归并排序是将数列a[l,h]分成两半a[l,mid]和a[mid+1,h]分别进行归并排序,然后再将这两半合并起来。
在合并的过程中(设l<=i<=mid,mid+1<=j<=h),当a[i]<=a[j]时,并不产生逆序数;当a[i]>a[j]时,在前半部
分中比a[i]大的数都比a[j]大,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。因此,可以在归并排序中的合并
过程中计算逆序数.}
接下来就是代码了:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define rep(i,x,y) for(int i=x;i<=y;i++)
const int mod=99999997;
const int M=100010;
struct node{
int m,w;
}x[M],y[M];
bool cmp(node a,node b)
{
return a.m==b.m ? a.w<b.w : a.m<b.m;
}
int c[M],tmp[M];
int merge(int l,int r)
{
int ans,mid=(l+r)/2,top=1;
if(l>=r)return 0;
ans=(merge(l,mid) + merge(mid+1,r)) % mod;
for(int i=l,j=mid+1;i<=mid||j<=r;)
{
if(i<=mid && j<=r && c[i]>c[j])
{
tmp[top++]=c[j++];
ans+=mid-i+1;
}
else
if(i>mid)tmp[top++]=c[j++];
else tmp[top++]=c[i++];
}
rep(i,1,top-1)c[l+i-1]=tmp[i];
return ans % mod;
}
int main()
{
int n;
scanf("%d",&n);
rep(i,1,n)scanf("%d",&x[i].m),x[i].w=i;
sort(x+1,x+n+1,cmp);
rep(i,1,n)scanf("%d",&y[i].m),y[i].w=i;
sort(y+1,y+n+1,cmp);
rep(i,1,n)
c[x[i].w]=y[i].w;
printf("%d",merge(1,n));
return 0;
}