归并排序求逆序对 CODEVS 1688 && NOIP 2013 火柴排队

昨天做火柴排队跪掉了啊。

归并排序的思想就是分治,把区间分成小的部分排好序再把小的区间合并起来排序,最终情况是整个区间被分割成两个部分,这两个部分已经完全有序,然后把这两个部分再合并起来。

逆序对,就是在一个序列中,如果ai>aj&&i< j则这是一个逆序对。

用归并排序合并的时候,如果右边区间的一个元素(称为r)小于左边区间的一个元素(称为l),则左边区间从l往后的所有元素都大于r, 所以r是一定要移动到l以前的,用左区间的右界的指针减去当前左区间左界的指针+1,就是r这个元素当前产生的逆序对数
比如:
左区间:3 4 5 右区间:0 1 2
0是小于3的,所以0小于左区间从3往后的所有数字,所以0当前产生的逆序对数为左区间右界指针:3减去当前左区间指针:1+1==3个。

CODEVS 1688
题目描述 Description
给定一个序列a1,a2,…,an,如果存在i< j并且ai>aj,那么我们称之为逆序对,求逆序对的数目

数据范围:N<=10^5。Ai<=10^5。时间限制为1s。

#include<algorithm>
#include<cstdio>
using namespace std;
const int maxn=1e7+500;
typedef long long ll;
int tmp[maxn];
ll numb[maxn];
ll ans;
void gui(ll l,ll llll,ll r,ll rr)
{
    ll poi=l;
    ll lll=l; 
    while(l<=llll&&r<=rr)
    {
        if(numb[l]<=numb[r])
            tmp[poi++]=numb[l],l++;
        else
            tmp[poi++]=numb[r],ans+=(llll-l+1),r++;
    }
    while(l<=llll)
        tmp[poi++]=numb[l],l++;
    while(r<=rr)
        tmp[poi++]=numb[r],r++;

    for(ll i=lll;i<=rr;i++)
        numb[i]=tmp[i];
}
void divv(ll L,ll R)
{
    ll mid;
    if(L<R)
    {
        ll mid=L+R>>1;
        divv(L,mid);
        divv(mid+1,R);
        gui(L,mid,mid+1,R);
    }
}
ll n;
int main()
{
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++)
        scanf("%lld",&i[numb]);

    divv(1,n);
    printf("%lld\n",ans);
    return 0;
}

match火柴排队
题目描述 Description
涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:
i=1n(a[i]b[i])2
,其中 ai表示第一列火柴中第 i 个火柴的高度,bi表示第二列火柴中第 i 个火柴的高度。
每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997 取模的结果。
输入描述 Input Description
共三行,第一行包含一个整数 n,表示每盒中火柴的数目。
第二行有 n 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。
第三行有 n 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。
输出描述 Output Description
输出共一行,包含一个整数,表示最少交换次数对 99,999,997 取模的结果。
【数据范围】
对于 10%的数据, 1 ≤ n ≤ 10;
对于 30%的数据,1 ≤ n ≤ 100;
对于 60%的数据,1 ≤ n ≤ 1,000;
对于 100%的数据,1 ≤ n ≤ 100,000,0 ≤火柴高度≤ 2^31 - 1。

简而言之,就是a中第i大的元素和b中第i大的元素一 一对应则可以使上面的式子最小。

我们用lower_bound把a和b都离散化一下,得到的序列是ai/bi这个元素在原序列中是第ai大的。

我们要把一个序列变成想要的序列,就是让a,b的第i大的元素一一对应。
该怎么办?排序啊。
求交换次数?逆序对啊。

怎么对b进行排序?
我们要以a为关键字进行排序,把a中的元素扔到一个桶里,即tong[numa[i]]=i
然后归并的时候比较的是tong[numb[l]]和tong[numb[r]],即比较的是numb中的元素在numa中的位置,这样就完成了按关键字排序。

#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long ll;
const ll ha=99999997;
const ll maxn=100000+500;
ll n;
ll a[maxn];
ll b[maxn];
ll numa[maxn];
ll numb[maxn];
ll anss=0;
ll tong[maxn];
ll tmp[maxn];
void gui(ll l,ll llll,ll r,ll rr)
{
    ll poi=l;
    ll lll=l; 
    while(l<=llll&&r<=rr)
    {
        if(tong[numb[l]]<tong[numb[r]])
            tmp[poi++]=numb[l],l++;
        else
            tmp[poi++]=numb[r],anss+=(llll-l+1),anss%=ha,r++;
    }

    while(l<=llll)
        tmp[poi++]=numb[l],l++;
    while(r<=rr)
        tmp[poi++]=numb[r],r++;

    for(ll i=lll;i<=rr;i++)
        numb[i]=tmp[i];
}
void divv(ll L,ll R)
{
    ll mid;
    if(L<R)
    {
        ll mid=L+R>>1;
        divv(L,mid);
        divv(mid+1,R);
        gui(L,mid,mid+1,R);
    }
}
int main()
{
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++)
        scanf("%lld",&i[a]),i[numa]=i[a];
    sort(a+1,a+n+1);
    for(ll i=1;i<=n;i++)
        i[numa]=lower_bound(a+1,a+1+n,i[numa])-a;
    for(ll i=1;i<=n;i++)
        scanf("%lld",&i[b]),i[numb]=i[b];
    sort(b+1,b+n+1);
    for(ll i=1;i<=n;i++)
        i[numb]=lower_bound(b+1,b+1+n,i[numb])-b;
    for(ll i=1;i<=n;i++)
        i[numa][tong]=i;

    divv(1,n);

    printf("%lld",anss%ha);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值