noip2013 火柴排序

一般来说我是很少写noip题解的:

但是这道题困扰了我很久(因为我太弱啦,事实上这只是一道傻逼题)

题目链接https://vijos.org/p/1842


题目大意:我就不说了,汉语大家都能看懂


题解:这道题的实质是用归并排序求逆序对,这是为什么呢?

首先,我们很容易猜想将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;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值