zhao_ry514114
赵若伊

导航

 

思路

先观察一对相邻的数 \(x,y\):

如果 \(x,y\) 奇偶性相同,那么 \(\left\lfloor\frac{x+y}{2}\right\rfloor=\frac{x+y}{2}\)。

如果 \(x,y\) 奇偶性不同,那么 \(x+y\) 为奇数,有 \(\left\lfloor\frac{x+y}{2}\right\rfloor=\frac{x+y-1}{2}\)。

因此可以统一写成 \(\left\lfloor\frac{x+y}{2}\right\rfloor=\frac{x+y-[x,y\text{ 奇偶不同}]}{2}\)。

设最终排列为 \(p_1,p_2,\dots,p_n\),其中相邻奇偶性不同的对数为 \(cnt\)。

那么原式为 \(\frac{\sum_{i=1}^{n-1}(p_i+p_{i+1})-cnt}{2}\)。

注意每个中间位置都会出现两次,而首尾只出现一次,因此

\(\sum_{i=1}^{n-1}(p_i+p_{i+1})=2\sum_{i=1}^n p_i-p_1-p_n\)。

于是答案变为 \(\sum_{i=1}^n p_i-\frac{p_1+p_n+cnt}{2}\)。

数组总和是固定的,所以我们真正需要做的是:

最大化 \(p_1+p_n+cnt\)。


首尾怎么选

对于确定的首尾奇偶性,\(cnt\) 只与整个排列的奇偶顺序有关,与具体数值无关。

所以如果首尾都要求是奇数,那么显然应该选择最大的两个奇数作为首尾。

同理:

  • 首尾都是奇数:选择最大的两个奇数;
  • 首尾都是偶数:选择最大的两个偶数;
  • 首尾一奇一偶:分别选择最大的奇数和最大的偶数。

因此只需要考虑这三种情况。


中间怎么排

确定首尾以后,我们希望让相邻位置尽可能多地出现一奇一偶,因为每出现一次奇偶不同,就可以让答案减少 \(\frac12\)。

所以中间的数应该尽可能交替放置:

奇 偶 奇 偶 奇 偶 ...

或者:

偶 奇 偶 奇 偶 奇 ...

具体数值大小并不影响奇偶不同的数量,因此剩余的奇数和偶数按从小到大的顺序取即可。

代码中分别讨论三种首尾情况。

情况一:首尾都是奇数

先把最大的奇数放在开头,把第二大的奇数留到最后。

剩余位置尽量按照:

偶 奇 偶 奇 ...

的顺序放置。

d.eb (b[b.size () - 1]);

while (id1 + 2 < b.size () || id2 < c.size ()) {
    if (id2 < c.size ()) d.eb (c[id2 ++]);
    if (id1 + 2 < b.size ()) d.eb (b[id1 ++]);
}

d.eb (b[b.size () - 2]);

情况二:首尾都是偶数

同理,把最大的两个偶数放在首尾,中间尽量按照:

奇 偶 奇 偶 ...

排列。

情况三:首尾一奇一偶

分别把最大的奇数和最大的偶数放在两端,中间继续尽量奇偶交替。

三种情况分别计算答案,最后取最小值即可。


如何想到的

看到下取整后,可以先考虑它什么时候会真正产生影响。

对于两个数 \(x,y\),只有当它们一奇一偶时,\(x+y\) 才是奇数,此时 \(\left\lfloor\frac{x+y}{2}\right\rfloor\) 会比直接除以 \(2\) 少 \(\frac12\)。

所以自然会想到,我们应该尽量让相邻的两个数奇偶性不同。

继续把整个式子展开以后,会发现除了这个“奇偶不同的次数”以外,所有中间元素都会被计算两次,只有首尾元素只计算一次。

因此问题就被拆成了两个部分:

  1. 首尾尽量放大的数;
  2. 中间尽量让奇偶交替。

而首尾又只有“奇奇、偶偶、奇偶”三种可能,于是分别枚举这三种情况即可。

代码

#include <bits/stdc++.h>
#define rep(i,j,k) for (int i = (j); i <= (k); i ++)
#define per(i,j,k) for (int i = (j); i >= (k); i --)
#define uint unsigned int
#define ll long long
#define ull unsigned long long
#define db double
#define ldb long double
#define mkp make_pair
#define eb emplace_back
#define e_ emplace
#define ls (p << 1)
#define rs (p << 1 | 1)
//#define int ll
using namespace std;
const int N = 2e5 + 5;
//const int MOD = 998244353;
//const int MOD = 1000000007;
const int INF = 0x3f3f3f3f;
const ll LINF = 4e18;
int T;
int n;
int a[N];
vector<int> b, c, d;

signed main () {
    //freopen (".in", "r", stdin);
    //freopen (".out", "w", stdout);
    ios::sync_with_stdio (0);
    cin.tie (0);
    cin >> T;
    while (T --) {
        b.clear ();
        c.clear ();
        cin >> n;
        rep (i, 1, n) {
            cin >> a[i];
            if (a[i] % 2) b.eb (a[i]);
            else c.eb (a[i]);
        }

        int id1 = 0, id2 = 0;
        ll mn = LINF;

        sort (b.begin (), b.end ());
        sort (c.begin (), c.end ());

        if (b.size () >= 2) {
            d.clear ();
            d.eb (b[b.size () - 1]);

            while (id1 + 2 < b.size () || id2 < c.size ()) {
                if (id2 < c.size ()) d.eb (c[id2 ++]);
                if (id1 + 2 < b.size ()) d.eb (b[id1 ++]);
            }

            d.eb (b[b.size () - 2]);

            ll ans = 0;
            rep (i, 1, d.size () - 1)
                ans += (d[i] + d[i - 1]) / 2;

            mn = min (mn, ans);
        }

        if (c.size () >= 2) {
            d.clear ();
            id1 = id2 = 0;
            d.eb (c[c.size () - 1]);

            while (id1 < b.size () || id2 + 2 < c.size ()) {
                if (id1 < b.size ()) d.eb (b[id1 ++]);
                if (id2 + 2 < c.size ()) d.eb (c[id2 ++]);
            }

            d.eb (c[c.size () - 2]);

            ll ans = 0;
            rep (i, 1, d.size () - 1)
                ans += (d[i] + d[i - 1]) / 2;

            mn = min (mn, ans);
        }

        if (c.size () && b.size ()) {
            d.clear ();
            id1 = id2 = 0;
            d.eb (b[b.size () - 1]);

            while (id1 + 1 < b.size () || id2 + 1 < c.size ()) {
                if (id2 + 1 < c.size ()) d.eb (c[id2 ++]);
                if (id1 + 1 < b.size ()) d.eb (b[id1 ++]);
            }

            d.eb (c[c.size () - 1]);

            ll ans = 0;
            rep (i, 1, d.size () - 1)
                ans += (d[i] + d[i - 1]) / 2;

            mn = min (mn, ans);
        }

        cout << mn << '\n';
    }

    return 0;
}

复杂度

每组数据需要分别对奇数和偶数排序,总时间复杂度为 \(O(N\log N)\)。

构造排列和计算答案都是 \(O(N)\)。

posted on 2026-09-11 19:12  zhao_ry514114  阅读(7)  评论(0)    收藏  举报