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

题解 CF666D 【Chain Reaction】

2020-02-04 22:11:36


枚举每个点是走横坐标还是走纵坐标,得到 4 条直线, 要求放置的每个点要占据一条直线,将直线去重,分类讨论:


  1. 两条横线两条竖线

放置的点一定是四个交点,判断一下是否是正方形然后统计。 统计的话直接 $O(4!)$ 枚举点之间的配对方式即可。


  1. 两条横线一条竖线(一条横线两条竖线同)

首先两个交点一定要放,那么确定了正方形边长为两条横线的距离。 枚举剩下两个点在左边还是右边,分别统计。


  1. 两条横线(两条竖线同)

四个点都要放在两条平行线上,显然一条直线放两个点, 那么可以确定正方形边长 $D$ ,两个横坐标是形如 $x$ 和 $x + D$ 这样的。

求出最优的 $x$ ,一个简单的方式是三分, 因为不难发现答案是关于 x 的单峰函数。

更优秀的方式是直接解式子。

设第一条直线原有的两个点横坐标为 $x_1$, $x_2$ ,另外一条为 $x_3$, $x_4$ 。

在每条直线上枚举配对方式, 不妨设 $x_1$, $x_2$, $x_3$, $x_4$ 分别与 $x$, $x + D$, $x$, $x + D$ 配对。

那么这部分的答案就是 $max(|x_1 - x|, |x_2 - x - D|, |x_3 - x|, |x_4 - x - D|)$

令 $x_5 = x_2 - D$, $x_6 = x_4 - D$ ,上式为$max(|x_1 - x|, |x_5 - x|, |x_3 - x|, |x_6 - x|)$

这个式子也就是数轴上 $x$ 到四个位置的距离的最大值, 取这四个位置的最大最小值的平均数为 $x$ 即可令这个值最小。


至于其他情况不难证明都是不存在合法方案的。


参考实现:

#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; }
    inline operator char () { char x[3]; return scanf("%s", x), *x; }
} read;

int calc(int *x, int *y, int *nx, int *ny, int *ansp) {
    int p[4] = {0, 1, 2, 3}, res = 1000000000;
    do {
        int now = 0;
        for(int i = 0; i < 4; i ++)
            if(x[i] == nx[p[i]])
                now = std::max(now, std::abs(y[i] - ny[p[i]]));
            else if(y[i] == ny[p[i]])
                now = std::max(now, std::abs(x[i] - nx[p[i]]));
            else
                now = 1000000000;
        if(now < res) {
            res = now;
            for(int i = 0; i < 4; i ++) ansp[i] = p[i];
        }
    } while(std::next_permutation(p, p + 4));
    return res;
}

int main() {
    const int _ = 20200202; // magic
    int T = read;

    while(T --) {
        int x[5], y[5];
        for(int i = 0; i < 4; i ++) {
            x[i] = read;
            y[i] = read;
        }
        x[4] = 1;
        y[4] = 0;

        int ans = 1000000000;

        int ansx[4], ansy[4];
        int nx[4], ny[4];

        auto update = [&]() {
            int p[4] = {0, 1, 2, 3};
            int tmp = calc(x, y, nx, ny, p);
#if 0
            debug(">>>  get %d\n", tmp);
            for(int i = 0; i < 4; i ++)
                debug("%d %d\n", nx[p[i]], ny[p[i]]);
#endif
            if(tmp < ans) {
                ans = tmp;
                if(x[4] == 1)
                    for(int i = 0; i < 4; i ++) {
                        ansx[i] = nx[p[i]];
                        ansy[i] = ny[p[i]];
                    }
                if(x[4] == 0)
                    for(int i = 0; i < 4; i ++) {
                        ansx[i] = ny[p[i]];
                        ansy[i] = nx[p[i]];
                    }
            }
        };

        for(int S = 0; S < 16; S ++) {
            int xx[4] = {_, _, _, _}, yy[4] = {_, _, _, _};
            for(int i = 0; i < 4; i ++) {
                int *tar = (S >> i & 1) == x[4] ? xx : yy;
                int v = (S >> i & 1) == x[4] ? x[i] : y[i];
                int j = 0;
                while(tar[j] != _ and tar[j] != v) ++ j;
                tar[j] = v;
            }

            if(xx[2] != _ or yy[2] != _) continue;
            if(xx[1] == _ and yy[1] == _) continue;

            if(yy[0] == _) {
                std::swap(xx, yy);
                std::swap(x, y);
            }

            if(xx[0] == _) {
                int d = std::abs(yy[0] - yy[1]);
                ny[0] = ny[2] = yy[0];
                ny[1] = ny[3] = yy[1];

                for(int T = 0; T < 4; T ++) {
                    int shit[2] = {T >> 1, T & 1};
                    int l = 1000000000, r = - 1000000000;
                    for(int i = 0; i < 4; i ++) {
                        int o = y[i] == yy[1];
                        int v = shit[o] ? x[i] + d : x[i];
                        shit[o] ^= 1;
                        l = std::min(l, v);
                        r = std::max(r, v);
                    }

                    // debug("fcuk %d %d %d\n", d, l, r);
                    int mid = (l + r + 1) >> 1;
                    nx[0] = nx[1] = mid;
                    nx[2] = nx[3] = mid - d;
                    update();
                }
                continue;
            }

            if(yy[1] == _) {
                std::swap(xx, yy);
                std::swap(x, y);
            }

            if(xx[1] == _) {
                int d = std::abs(yy[0] - yy[1]);
                nx[0] = nx[1] = xx[0];
                ny[0] = ny[2] = yy[0];
                ny[1] = ny[3] = yy[1];

                nx[2] = nx[3] = xx[0] - d;
                update();
                nx[2] = nx[3] = xx[0] + d;
                update();
                continue;
            }

            if(std::abs(xx[0] - xx[1]) == std::abs(yy[0] - yy[1])) {
                nx[0] = nx[1] = xx[0];
                nx[2] = nx[3] = xx[1];
                ny[0] = ny[2] = yy[0];
                ny[1] = ny[3] = yy[1];
                update();
            }
        }

        if(ans == 1000000000) puts("-1");
        else {
            printf("%d\n", ans);
            for(int i = 0; i < 4; i ++)
                printf("%d %d\n", ansx[i], ansy[i]);
        }
    }
}