[NOIP2013提高&洛谷P1966]火柴排队 题解(树状数组求逆序对)

[NOIP2013提高&洛谷P1966]火柴排队

Description

涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为: ∑(ai-bi)^2
其中 ai 表示第一列火柴中第 i 个火柴的高度,bi 表示第二列火柴中第 i 个火柴的高度。
每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997 取模的结果。

输入格式:
共三行,第一行包含一个整数 n,表示每盒中火柴的数目。
第二行有 n 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。
第三行有 n 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。

输出格式:
输出共一行,包含一个整数,表示最少交换次数对 99,999,997 取模的结果。

Solution

1.要是两列火柴距离最小,那么我们就应让所有的(ai-bi)^2最小,即ai与bi的差最小,考虑贪心,应该让在序列中从小到大排序后序号相同的数相减,因为如果两数列中两个数在各自数列中从小到大序号相等,那么如果这两个数不做差分别与另一个数做差,差的平方显然是要比原来大的。

2.那么我们考虑如何将数列整理成想要的:我们建立一个新的数列,以离散后的第一个数列该项为下标,以离散后第二个数列对应的项为值建立,相当于以第一个数列为关键字排序第二个数列,然后跑一遍逆序对即可。

树状数组求逆序对的方法参考我的博文:http://www.cnblogs.com/COLIN-LIGHTNING/p/8621294.html

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;

int now[100100],c[100100],n,m,i,j,k;
struct nums{
    int num,d;
}a1[100100],a2[100100];

inline int rd(){
    int x=0;
    char c=getchar();
    bool f=false;
    while(!isdigit(c)){ 
        if(c=='-') f=true;
        c=getchar();
    }
    while(isdigit(c)){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return f?-x:x;
}

inline int lowbit(int x){return x&-x;}

void add(int x){
    while(x<=n){
        c[x]+=1;
        x+=lowbit(x);
    }
}

int sum(int x){
    int ret=0;
    while(x>0){
        ret+=c[x];
        x-=lowbit(x);
    }
    return ret;
}

bool cmp(nums x,nums y){
    return x.num<y.num;
}
int main(){
    memset(c,0,sizeof(c));
    n=rd();
    for(i=1;i<=n;++i){
        a1[i].num=rd();
        a1[i].d=i;
    }
    for(i=1;i<=n;++i){
        a2[i].num=rd();
        a2[i].d=i;
    }
    sort(a1+1,a1+1+n,cmp);
    sort(a2+1,a2+1+n,cmp);
    for(i=1;i<=n;++i) now[a1[i].d]=a2[i].d;
    long long ans=0;
    for(i=n;i>0;--i){
        add(now[i]);
        ans=(ans+sum(now[i]-1))%99999997;
    }
    printf("%lld\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/COLIN-LIGHTNING/p/8621855.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值