• 火柴排队(树状数组求逆序对)


    火柴排队

    来源:
    2013年NOIP全国联赛提高组
    题目描述:
    涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:这里写图片描述
    ,其中 ai表示第一列火柴中第 i 个火柴的高度,bi表示第二列火柴中第 i 个火柴的高度。
    每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997 取模的结果。
    输入描述:
    共三行,第一行包含一个整数 n,表示每盒中火柴的数目。
    第二行有 n 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。
    第三行有 n 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。
    输出描述:
    输出共一行,包含一个整数,表示最少交换次数对 99,999,997 取模的结果。
    样例输入:
    [Sample 1]
    4
    2 3 1 4
    3 2 1 4
    [Sample 2]
    4
    1 3 4 2
    1 7 2 4
    样例输出:
    [Sample 1]
    1
    [Sample 2]
    2
    数据范围及提示:
    【样例1说明】
    最小距离是 0,最少需要交换 1 次,比如:交换第 1 列的前 2 根火柴或者交换第 2 列的前 2 根火柴。
    【样例2说明】
    最小距离是 10,最少需要交换 2 次,比如:交换第 1 列的中间 2 根火柴的位置,再交换第 2 列中后 2 根火柴的位置。
    【数据范围】
    对于 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])^2,拆开为:a^2+b^2-2ab,若使该式尽可能小,则2ab应尽可能大。那么下一步就考虑如何使a[i]*b[i]尽可能大,根据排序不等式:反序和≤乱序和≤同序和 可得:a[i]*b[i]按照“同序”的方式排列起来是最大的,那么什么是同序?举个例子:在有序数列1、2、3和有序数列4、5、6中,1*4,2*5,3*6就是同序,1*4+2*5+3*6就是同序和,所以应该先排序!那么目前的这个问题就解决了,那么解决这个问题是用来做什么的呢?答案是:找到一种两个数列之间的对应关系,求逆序对!使a中的第几大对应b中的第几大(这就是排序的目的),然后生成数列,在生成的数列中找出逆序对的个数,就是最终的答案了。
    举个例子:
    火柴a:1 2 4 3
    火柴b:1 4 2 3
    显然只需交换一步就出答案,答案为:1
    如何得出:
    把火柴a看为不动的,只移动火柴b。
    排序之后:
    火柴a为:1(1)、2(2)、3(4)、4(3),
    同样b为:1(1)、2(3)、3(4)、4(2)。
    (括号中的为该数字在原数列中的id)
    然后以排序后a中元素的id为下标,b中元素的下标为元素,生成数列x
    x[1]=1 x[2]=3 x[4]=4 x[3]=2
    也就是:x[4]={1 3 2 4}
    x中逆序对为{3,2},仅此一对,所以ans=1.
    完成!

    下面给出树状数组求逆序对的代码:

    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int mod=99999997;
    const int maxn=100010;
    struct node
    {
        int id;
        int sum;
    }a[maxn],b[maxn];
    int la[maxn],lb[maxn];
    int n,ans,c[maxn],x[maxn];
    bool cmp(node x,node y)
    {
        return x.sum<y.sum;
    }
    int lowbit(int x)
    {
        return x&(-x);
    }
    void add(int x)
    {
        for(int i=x;i<=n;i+=lowbit(i))
        c[i]++;
    }
    int getsum(int x)
    {
        int sum=0;
        for(int i=x;i;i-=lowbit(i))
        sum=(sum+c[i])%mod;
        return sum%mod;
    }
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        cin>>a[i].sum,a[i].id=i;
        for(int i=1;i<=n;i++)
        cin>>b[i].sum,b[i].id=i;
        sort(a+1,a+n+1,cmp);
        sort(b+1,b+n+1,cmp);
        for(int i=1;i<=n;i++)//找一种对应关系,生成数列x
        x[a[i].id]=b[i].id;
        for(int i=n;i>=1;i--)//求数列x中的逆序对个数,得出答案
        {
            add(x[i]);
            ans=(ans+getsum(x[i]-1))%mod;
        }
        cout<<ans%mod;
        return 0;
    }
  • 相关阅读:
    C# 窗体间传值方法大汇总(转)
    STM32 配置PC13~PC15
    STM32的USART发送数据时如何使用TXE和TC标志
    STM32_NVIC寄存器详解
    protel99se 问题汇总(不定期更新)
    STM32串口IAP实验笔记
    Keil MDK下如何设置非零初始化变量(复位后变量值不丢失)
    STM32定时器配置(TIM1-TIM8)高级定时器+普通定时器,定时计数模式下总结
    帮助类-AD域操作
    GitHub贡献第一的公司是谁?微软开源软件列表
  • 原文地址:https://www.cnblogs.com/cax1165/p/6070980.html
Copyright © 2020-2023  润新知