NOIp2013 火柴排队

Posted zcdhj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIp2013 火柴排队相关的知识,希望对你有一定的参考价值。

Luogu

其实只用交换一列火柴就行了

我们用一种套路求出来怎样才是最优的高度

不难得知,当第一列火柴以最优的结果排列,再怎么交换都不会使答案更优

也就是 \((a_i-b_i)^2+(a_{i+1}-b_{i+1})^2<(a_{i+1}-b_i)^2+(a_i-b_{i+1})^2\)

整理得到 \(a_ib_i+a_{i+1}b_{i+1}>a_ib_{i+1}+a_{i+1}b_i\)

充分发挥人类智慧,就可以发现当 \(a_i<a_{i+1},b_i<b_{i+1}\) 时答案最优

又因为对 \(i\) 位置答案有贡献的只有 \(a_i,b_i\) 所以当 \(a,b\) 的排列顺序一样时也是最优的

\(a,b\) 都离散化一下,就只要将 \(a\) 移移移成 \(b\) 就行了。

那不就是相当于重新定义了大小的逆序对吗?

再把 \(a\)\(b\) 来手玩一下新的大小,求一下逆序对数就是答案了。

下面将蒯过来某dalao对于"为什么最少交换次数是逆序对数"这个问题的解答

1.如果这个序列不存在最小逆序对,那么这个序列有序(即逆序对为0),用反证法证明就是如果有两个不相邻的逆序元素那么中间的元素会里外不是人,所以这个序列必须有序

2.每次交换两个相邻元素,逆序对只变动1,证明就是,如果有别的元素和这两个的逆序关系发生了也发生了变化可以通过分情况讨论证明这种情况不存在

那么我们将这个序列排序的手段就是不停的交换两个最小逆序对,每次逆序对变动1所以最后排序的总数一定是逆序对数

其他方案一定在某一步中交换了一个顺序对,这会导致逆序对的个数增加,一定不优,证明完毕……
#include <iostream>
#include <cstdio>
#include <algorithm>

const int MaxN = 1e5 + 5;
const int Mod = 99999997;

int N, Tot, Ans;
int A[MaxN], B[MaxN], C[MaxN];

inline int read()
{
    register int x = 0;
    register char ch = getchar();
    while(!isdigit(ch)) ch = getchar();
    while(isdigit(ch))
    {
        x = x * 10 + ch - ‘0‘;
        ch = getchar();
    }
    return x;
}

void merge_sort(int l, int r)
{
    if(l == r) return;
    int mid = l + r >> 1;
    merge_sort(l, mid);
    merge_sort(mid + 1, r);
    for(int i = l, l1 = l, l2 = mid + 1; i <= r; ++i)
    {
        if((A[l1] < A[l2] && l1 <= mid ) || l2 > r) C[i] = A[l1++];
        else
        {
            (Ans += (mid - l1 + 1)) %= Mod;
            C[i] = A[l2++];
        }
    }
    for(int i = l; i <= r; ++i) A[i] = C[i];
}

int main()
{
    N = read();
    for(int i = 1; i <= N; ++i) A[i] = read();
    for(int i = 1; i <= N; ++i) B[i] = read();
    for(int i = 1; i <= N; ++i) C[i] = A[i];
    std::sort(&C[1], &C[N + 1]);
    Tot = std::unique(&C[1], &C[N + 1]) - &C[1];
    for(int i = 1; i <= N; ++i) A[i] = std::lower_bound(&C[1], &C[Tot + 1], A[i]) - &C[0];
    for(int i = 1; i <= N; ++i) C[i] = B[i];
    std::sort(&C[1], &C[N + 1]);
    Tot = std::unique(&C[1], &C[N + 1]) - &C[1];
    for(int i = 1; i <= N; ++i) B[i] = std::lower_bound(&C[1], &C[Tot + 1], B[i]) - &C[0];
    for(int i = 1; i <= N; ++i) C[B[i]] = i;
    for(int i = 1; i <= N; ++i) A[i] = C[A[i]];
    merge_sort(1, N);
    printf("%d\n", Ans);
    return 0;
}

以上是关于NOIp2013 火柴排队的主要内容,如果未能解决你的问题,请参考以下文章

noip2013 火柴排队

NOIp2013 火柴排队

NOIp2013火柴排队

NOIP2013 火车排队

NOIP2013提高组 T2 火柴排队

noip2013提高组day1第二题火柴排队