仅用于(也专用于)写题解,其他的去 kewth.github.io 玩啊~~

题解 CF1286A 【Garland】

2020-01-08 10:32:38


提供一个效率优秀的贪心做法。

首先排列只需要考虑奇偶性,设未确定的数中有 a 个奇数 b 个偶数。
对于两个中间全是 0 的已经确定的数,考虑怎样安排中间的 0 能使答案尽量小。
设这两个数模 2 为 x, y ,中间有 k 个数未确定。 分类讨论。

如果 x != y ,无论中间放什么数,都可以调整位置(只要和 x 奇偶性相同的在 x 一侧,其他在另一侧)使得对答案贡献恰好为 1 。

如果 x = y ,那么中间如果能全部放与 x, y 奇偶性相同的数就不会增加答案,
但这样会付出一定代价,代价就是必须用 k 个 a (或 b )。
而如果中间不全部放与 x, y 奇偶性相同的数的话,无论放什么数,
都可以调整位置(只要奇数在一块偶数在一块即可)使得对答案恰好贡献 2 。

那么不难发现需要决策的部分只有 x = y 中间全放一样的数这种情况,
而且有代价和收益,收益都是 2 ,代价不等,对于两种奇偶性能承受的代价分别为 a, b 。
将这些决策按代价排序,尽量选择代价小的去填,即可得到最大收益,即最小答案。

边界问题在于一些前缀 0 和一些后缀 0 难以判断,
直接暴力枚举钦定 p[0] 和 p[n + 1] 的奇偶性即可。

复杂度 $O(n)$ ,排序部分用基数排序。

然而这题 n 只有 100 。。。表示不满。

#include <cstdio>
#include <algorithm>
#define debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long ll;

struct {
    inline operator int () { int x; return scanf("%d", &x), x; }
    inline operator ll () { ll x; return scanf("%lld", &x), x; }
    template<class T> inline void operator () (T &x) { x = *this; }
    template<class T, class ...A> inline void operator () (T &x, A &...a)
    { x = *this; this -> operator ()(a...); }
} read;

const int maxn = 105;
int p[maxn], tot[2];
int q[2][maxn];

int solve(int n) {
    int las = 0;
    int res = 0;
    for(int i = 1; i <= n + 1; i ++)
        if(p[i]) {
            if(p[i] == p[las])
                ++ q[p[i] - 2][i - las - 1];
            else
                ++ res;
            las = i;
        }

    for(int i = 0; i < 2; i ++) {
        int max = tot[i];
        for(int x = 0; x <= max; x ++) {
            while(q[i][x] and x <= max) {
                max -= x;
                -- q[i][x];
            }
        }
    }

    for(int i = 0; i <= n; i ++) {
        res += 2 * (q[0][i] + q[1][i]);
        q[0][i] = q[1][i] = 0;
    }

    return res;
}

int main() {
    int n = read;
    if(n == 1) return puts("0"), 0;

    tot[0] = n >> 1;
    tot[1] = (n + 1) >> 1;

    for(int i = 1; i <= n; i ++) {
        int x = read;
        if(x) {
            p[i] = 2 + (x & 1);
            -- tot[p[i] - 2];
        }
    }

    int ans = 1000000000;
    for(int i = 2; i <= 3; i ++)
        for(int j = 2; j <= 3; j ++) {
            p[0] = i;
            p[n + 1] = j;
            ans = std::min(ans, solve(n));
        }

    printf("%d\n", ans);
}