洛谷 P1966 火柴排队 解题报告

P1966 火柴排队

题目描述

涵涵有两盒火柴,每盒装有 (n) 根火柴,每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为: (sum (a_i-b_i)^2)其中 (a_i) 表示第一列火柴中第 (i) 个火柴的高度, (b_i) 表示第二列火柴中第 (i) 个火柴的高度。

每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 (99,999,997) 取模的结果。

输入输出格式

输入格式:

共三行,第一行包含一个整数 (n) ,表示每盒中火柴的数目。

第二行有 (n) 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。

第三行有 (n) 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。

输出格式:

一个整数,表示最少交换次数对 (99,999,997) 取模的结果。

数据范围

对于 (10\%) 的数据, (1 ≤ n ≤ 10)

对于 (30\%) 的数据, (1 ≤ n ≤ 100)

对于 (60\%) 的数据, (1 ≤ n ≤ 1,000)

对于 (100\%) 的数据, (1 ≤ n ≤ 100,000),(0≤) 火柴高度 (≤) (maxlongint)


感觉蛮神奇的一道题目。

玩一下我们感觉,两边第(i)大的相互对着是最优的。

证明可以先拆平方,然后利用邻项交换证明最优性。

离散一下,我们发现其实就是求逆序对。

处理起来可能比较麻烦,但把所有数组都搞出来反而不容易错。


Code:

#include <cstdio>
#include <algorithm>
#define ll long long
const int N=100010;
struct node
{
    int pos;ll h;
    bool friend operator <(node n1,node n2)
    {
        return n1.h<n2.h;
    }
}a[N],b[N];
ll s[N],c[N],d[N],e[N],f[N],ans;
int n;
ll query(int x)
{
    ll sum=0;
    while(x)
    {
        sum+=s[x];
        x-=x&-x;
    }
    return sum;
}
void add(int x)
{
    while(x<=n)
    {
        s[x]++;
        x+=x&-x;
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i].h);
        a[i].pos=i;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&b[i].h);
        b[i].pos=i;
    }
    std::sort(a+1,a+1+n);
    std::sort(b+1,b+1+n);
    for(int i=1;i<=n;i++)
    {
        c[a[i].pos]=i;
        d[b[i].pos]=i;
    }
    for(int i=1;i<=n;i++)
        e[d[i]]=i;
    for(int i=1;i<=n;i++)
        f[i]=e[c[i]];
    for(int i=1;i<=n;i++)
    {
        (ans+=query(n)-query(f[i]))%=99999997;
        add(f[i]);
    }
    printf("%lld
",ans);
    return 0;
}


2018.8.5

原文地址:https://www.cnblogs.com/butterflydew/p/9427375.html