CF622D 排列数

1 CF622D 排列数

2 题目描述

时间限制 \(1s\) | 空间限制 \(256M\)

数组 \(a\) 包含两次从 \(1\)\(n\) 的所有整数。你可以任意排列 \(a\) 中的任何数字。
\(x_i,y_i(x_i < y_i)\) 表示数字 \(i\)\(a\) 中的位置。定义 \(d_i = y_i-x_i\) 为数字 \(i\) 之间的距离。排列数组 \(a\) 中的数字使得下面式子的和最小:
\(s = \sum_{i=1}^n\limits (n-i) * |d_i + i - n|\)

数据范围:\(1 ≤ n ≤ 5*10^5\)

3 题解

\(\sum_{i=1}^n\limits (n-i) * |d_i + i - n|\) 这个式子在所有的 \(d_i\) 都等于 \(n - i\) 时等于 \(0\)。那么我们如何构造出来一个满足所有 $d_i $ 都等于 \(n-i\) 的数列呢?我们发现:如果在 \(l, r\) 两个数中间插入 \(l+1, r-1\),这两个数之间隔的距离是 \(l, r\) 之间距离 \(+2\)。 也就是说,在 \(l+1, r-1\) 这个位置放的数必须是在 \(l, r\) 这个位置放的数 \(+2\)。也就是说,我们只需要如下构造,就可以求出正确的答案:

先放两个 \(1\),相隔 \(n-i-1\)。然后向中间慢慢加数,放的数从两个 \(3\) 一直到两个 \(n-1\)\(n\) 为偶数)或者一个 \(n\)\(n\) 为奇数)。随后两个是 \(2\),然后类似地往中间放数。最后一位是 \(n\)

4 代码(空格警告):

#include <iostream>
using namespace std;
int n, cnt;
bool flag, flag2;
int main()
{
    cin >> n;
    cnt = 1;
    while (1)
    {
        cout << cnt << " ";
        if (cnt == 1 && flag) break;
        if (n == 1) break;
        if (n == 2)
        {
            cout << cnt << " ";
            break;
        }
        else if (cnt == n)
        {
            flag = 1;
            cnt -= 2;
        }
        else if (cnt == n-1)
        {
            flag = 1;
            cout << cnt << " ";
            cnt -= 2;
        }
        else if (cnt < n && !flag) cnt += 2;
        else cnt -= 2;
    }
    cnt = 2;
    flag = 0;
    while (n >= 2)
    {
        cout << cnt << " ";
        if (cnt == 2 && flag) break;
        if (n == 2) break;
        if (n == 3)
        {
            cout << cnt << " ";
            break;
        }
        else if (cnt == n)
        {
            flag = 1;
            cnt -= 2;
        }
        else if (cnt == n-1)
        {
            flag = 1;
            cout << cnt << " ";
            cnt -= 2;
        }
        else if (cnt < n && !flag) cnt += 2;
        else cnt -= 2;
    }
    cout << n;
    return 0;
}

欢迎关注我的公众号:智子笔记

posted @ 2021-02-26 21:13  David24  阅读(106)  评论(0编辑  收藏  举报