1. 程式人生 > >洛谷 P1966 火柴排隊 解題報告

洛谷 P1966 火柴排隊 解題報告

efi 不容易 con scan can void 逆序對 return gin

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\n",ans);
    return 0;
}

2018.8.5

洛谷 P1966 火柴排隊 解題報告