zoukankan      html  css  js  c++  java
  • luogu1966 火柴排队

    题目大意

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

    题解

    预备知识

    • 因为任何a火柴的交换效果都与b交换火柴相同,所以我们只让b火柴移动。
    • 通过邻项交换的方法,我们可以得到一个序列的任意一个排列。

    贪心

      我们让序列$b$序列变换后的结果序列称为$b'$。那么对$forall i$,$a_i$在$a$中的大小排名与$b'_i$在$b'$中的相同。

      证明为邻项交换。如果$a_1$与$b_1$配对,$a_2$与$b_2$配对,$a_1leq a_2, b_1 leq b_2$,则这样得到的结果,比$a_1$和$b_2$、$a_2$和$b_1$配对得到的结果小。为了表示方便,我们令$x=a_1,y=b_1,a_2=x+k,b_2=y+t$,则前者的结果为$$(x-y)^2+(x+k-b-t)^2$$,后者结果为$$(a-b-t)^2+(a+k-b)^2$$。后者减去前者得到的结果为$$2kt$$,恒大于0。故原命题成立。

    如何得到$b'$?

      $b'$的要求是如果将$a$排序的变换为$f(a)$,则$f(b')$得到的序列也是递增的。也就是说,我们要得到$b'$,要先将$b$排序得到$b''$,随后$f^{-1}(b'')=b'$。那么达到$f^{-1}$的方法便是将$a$排序后的rank为下标,原来的id为值得到一个数组,随后将每个$b''$移到对应的id位置去即可。

    那么要交换多少次呢?

      即为逆序对数。用树状数组解决即可。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    #define ll long long
    const int MAX_N = 100010;
    const ll P = 99999997;
    int N;
    int NewId_OrgId[MAX_N];
    
    struct BIT
    {
    private:
        ll C[MAX_N];
    
        int Lowbit(int x)
        {
            return x & -x;
        }
    
    public:
        void Update(int p, ll delta)
        {
            while (p <= N)
            {
                C[p] += delta;
                p += Lowbit(p);
            }
        }
    
        ll Query(int p)
        {
            ll ans = 0;
            while (p > 0)
            {
                ans += C[p];
                p -= Lowbit(p);
            }
            return ans;
        }
    }g;
    
    struct Node
    {
        int Id, Val;
    
        bool operator < (const Node &a) const
        {
            return Val < a.Val;
        }
    }A[MAX_N], B[MAX_N], B1[MAX_N];
    
    int main()
    {
        scanf("%d", &N);
        for (int i = 1; i <= N; i++)
            A[i].Id = B[i].Id = i;
        for (int i = 1; i <= N; i++)
            scanf("%d", &A[i].Val);
        for (int i = 1; i <= N; i++)
            scanf("%d", &B[i].Val);
        sort(A + 1, A + N + 1);
        for (int i = 1; i <= N; i++)
            NewId_OrgId[i] = A[i].Id;
        sort(B + 1, B + N + 1);
        for (int i = 1; i <= N; i++)
            B1[NewId_OrgId[i]] = B[i];
        ll ans = 0;
        for (int i = N; i >= 1; i--)
        {
            ans = (ans + g.Query(B1[i].Id - 1)) % P;
            g.Update(B1[i].Id, 1);
        }
        printf("%lld
    ", ans);
        return 0;
    }
    

      

  • 相关阅读:
    hdu 2896 AC自动机模版题
    快递公司送货员送到货时,打电话通知客户来取的改进
    Scala数据类型中的Symbol(符号文本)
    hdu 3065 AC自动机模版题
    Oracle DB 复制数据库
    Java打包生成exe(使用exe4j和inno setup)
    C#的Lambda表达式嵌套例子
    WPF设置控件获得焦点FocusManager
    Winform给TextBox设置默认值(获取焦点后默认值消失)
    使用signtool.exe来验证程序的数字签名是否成功(命令行)
  • 原文地址:https://www.cnblogs.com/headboy2002/p/9784539.html
Copyright © 2011-2022 走看看