【noip 2013】火柴排队

去题面的传送门
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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值