mayoko’s diary

プロコンとかいろいろ。

Looksery Cup 2015 H. Degenerate Matrix

Looksery Cup 2015に参加しました。結果的にADHを通しました。レーティングは大幅に上がったのでハッピーです。

この問題は本番全く意味がわからないままサンプルを頼りにして通したんですが解法見て感動しました。

解法

Bの行列式が0になるということは,
B = \begin{bmatrix} x_1 & y_1\\x_2 & y_2\end{bmatrix}
として,
\begin{bmatrix} x_1 \\ y_1\end{bmatrix}\begin{bmatrix}x_2 \\ y_2\end{bmatrix}が平行であるということです。
一方で,
||A-B|| <= d_0が意味することは,
A = \begin{bmatrix} a & b\\c & d\end{bmatrix}として,
\begin{bmatrix} x_1 \\ y_1\end{bmatrix}が頂点\begin{bmatrix} a \\ b\end{bmatrix}を中心とする一辺の長さが2d_0の内部にあり,\begin{bmatrix}x_2 \\ y_2\end{bmatrix}が頂点\begin{bmatrix} c \\ d\end{bmatrix}を中心とする一辺の長さが2d_0の内部に存在するということです。

よって,d_0の長さを二分探索しながら最適解を求めることが出来ます(二分探索回はやってないんですが多分atanの値が存在するかどうかでtrue or falseを分けるんですかね)。

ですが,実は二分探索をしなくても一発で答えを求めることが出来ます。よく考えると,最適解に対して\begin{bmatrix} x_1 \\ y_1\end{bmatrix}\begin{bmatrix}x_2 \\ y_2\end{bmatrix}の関係としてありえるのは以下の4通りのみです。

f:id:mayokoex:20150609090533j:plain
(一つの正方形の右下ともう一方の正方形の左上を通る,etc)

これが意味することは,Bの行列の要素は
B = \begin{bmatrix} a+x & b+x\\c+x & d+x\end{bmatrix}
などとなるということです(他にもB = \begin{bmatrix} a-x & b+x\\c+x & d-x\end{bmatrix}などがあります)。

ということで,これらの場合にBの行列式が0になるのはどういう場合か考えてみます。例えばB = \begin{bmatrix} a+x & b+x\\c+x & d+x\end{bmatrix}の場合を考えてみると,

(a+x)(d+x) - (c+x)(b+x) = (ad-bc)-(a+b+c+d)x

となるので,x = \frac{ad-bc}{a+b+c+d}となります。実際にはあり得る場合について(a+b+c+d)のような値を求め,その最大値でad-bcを割った値が答えになります。

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    ll a, b, c, d;
    cin >> a >> b;
    cin >> c >> d;
    if (a == 0 && b == 0 && c == 0 && d == 0) {
        cout << 0 << endl;
        return 0;
    }
    ll A[4]; A[0] = a; A[1] = b; A[2] = c; A[3] = d;
    ll p = 0;
    for (int i = 0; i < 16; i++) {
        int l = (i&1) ^ ((i>>3)&1);
        int r = ((i>>1)&1) ^ ((i>>2)&1);
        if (l != r) continue;
        ll tmp = 0;
        for (int j = 0; j < 4; j++) {
            if ((i>>j)&1) {
                tmp += -1*A[j];
            } else {
                tmp += A[j];
            }
        }
        p = max(p, abs(tmp));
    }
    if (p != 0) {
        double x = abs(1. * (a*d-b*c) / p);
        printf("%.15lf\n", x);
    } else {
        cout << 0 << endl;
    }
    return 0;
}