「JOISC 2017 Day 1」开荒者

单独一个点,假如固定每个操作的数目,则得到的草呈矩形,且形状不会应操作顺序变化而变化。所以最后的结果与操作顺序无关。同时发现当向上、下次数总和一定时,若无上下边界,则草地形状一样。

考虑枚举向上、向下次数和,可以通过差分扫描线的方式维护出草地的形状(由于一个点只会被加入一次,删除一次,所以扫描线点数 \(O(n)\) 级别)。

考虑对于每一行对答案的贡献,设第 \(i\) 每一株草的位置时 \(a1 \dots a_m\) ,则向左向右对答案的贡献为 \(f_i = max(a_1 – 1, c – a_m, max_{i = 1}^{m – 1}(a{i + 1} – a{i})\) ,现在考虑上下的边界,答案为 \(min_{i}(max_{i \leq j \leq i + r – 1} f_j)\) ,可以用单调队列维护。
时间复杂度 \(O(n^3)\) .


#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int N = 605;

int R, C, n, lim, m, f[N][3], q[3][N], l[3], r[3];

bool vis[N][N];

set <int> s;

ll ans;

struct Node {
    int x, y;
} a[N], b[N];

bool cmpx_(Node a, Node b) {
    return a.x < b.x;
}

bool cmpy_(Node a, Node b) {
    return a.y < b.y;
}

void solve_(int k) {
    for (int i = 1; i <= n; i++)
        vis[k][i] = vis[k - 1][i];

    if (b[k].y > 0)
        vis[k][b[k].y] = 1;
    else
        vis[k][-b[k].y] = 0;

    f[k][0] = f[k][1] = f[k][2] = 0;

    for (int i = 1, j = 0; i <= n; i++)
        if (vis[k][i]) {
            f[k][2] = C - a[i].y;

            if (j)
                f[k][0] = max(f[k][0], a[i].y - a[j].y - 1);
            else
                f[k][1] = a[i].y - 1;

            j = i;
        }
}

void init_(int d) {
    sort(a + 1, a + n + 1, cmpy_);

    for (int i = 1; i <= n; i++) {
        b[++m] = (Node) {
            a[i].x - d, i
        };
        b[++m] = (Node) {
            a[i].x + 1, -i
        };
    }

    sort(b + 1, b + m + 1, cmpx_);

    for (int i = 1; i < m; i++)
        solve_(i);
}

ll calc_(int d) {
    for (int i = 1; i <= m; i++)
        if (b[i].y > 0)
            b[i].x -= d;

    bool ok = 1;

    while (ok) {
        ok = 0;

        for (int i = 1; i < m; i++)
            if (b[i].x > b[i + 1].x) {
                ok = 1, swap(b[i], b[i + 1]);
                solve_(i), solve_(i + 1);
            }
    }

    ll res = 4e9;

    for (int i = 0; i < 3; i++)
        l[i] = 1, r[i] = 0;

    for (int i = 1, j = 1; ; i++) {
        while (j < m && b[j].x - b[i].x < R) {
            if (b[j].x < b[j + 1].x) {
                for (int k = 0; k < 3; k++) {
                    while (l[k] <= r[k] && f[q[k][r[k]]][k] <= f[j][k])
                        r[k]--;

                    q[k][++r[k]] = j;
                }
            }

            j++;
        }

        if (b[j].x - b[i].x < R)
            break;

        for (int k = 0; k < 3; k++)
            while (l[k] <= r[k] && q[k][l[k]] < i)
                l[k]++;

        res = min(res, max(1ll * f[q[0][l[0]]][0], 1ll * f[q[1][l[1]]][1] + f[q[2][l[2]]][2]));
    }

    return res;
}

int main() {
    scanf("%d%d%d", &R, &C, &n), ans = R + C - 2;

    for (int i = 1; i <= n; i++)
        scanf("%d%d", &a[i].x, &a[i].y);

    sort(a + 1, a + n + 1, cmpx_);

    lim = a[1].x - 1 + R - a[n].x;

    for (int i = 1; i < n; i++)
        lim = max(lim, a[i + 1].x - a[i].x - 1);

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) {
            if (a[i].x - 1 + R - a[j].x >= lim)
                s.insert(a[i].x - 1 + R - a[j].x);

            if (a[j].x - a[i].x - 1 >= lim)
                s.insert(a[j].x - a[i].x - 1);
        }

    init_(*s.begin());

    int last = *s.begin();

    for (int x : s) {
        ans = min(ans, x + calc_(x - last)), last = x;

        if (ans <= last)
            break;
    }

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

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注