AOJ 2593 Square in Circles

解法

辺の長さをまず決め打つと、それぞれの円と交わる部分が区間として求まる。
得られた区間の連結な部分の総長さで、決めうった値よりも長い所があれば、そのような正方形を配置することができる。
これは O(n(logn)^2) で解ける。

しゃくとりとかスタックとか使ってうまくやれば O(nlogn) でも解けそうだけど、まあ定数倍もそこまでじゃないし楽な方を選択すれば良さそう。

ありえそうな間違いといえば、隣接する2つの円だけみて判定とか?
(3つ以上の円がかなり近い位置に並んでいると、横幅で3つ以上の円を利用できることがあるのでNG)

ソースコード

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

using pdd = pair<double, double>;

double solve(vector<double> x, vector<double> r) {
    const int n = x.size();
    auto check = [&] (double h) {
        vector<pdd> ps(n);
        for(int i = 0; i < n; ++i) {
            double x1 = x[i], x2 = x[i];
            if(h <= r[i]) {
                x1 -= sqrt(r[i] * r[i] - h * h);
                x2 += sqrt(r[i] * r[i] - h * h);
            }
            ps[i] = make_pair(x1, x2);
        }
        sort(begin(ps), end(ps));
        double lx = -1e9, rx = -2e9;
        for(int i = 0; i < n; ++i) {
            if(rx < ps[i].first) {
                if(rx - lx >= 2 * h) return true;
                lx = ps[i].first, rx = ps[i].second;
            } else {
                rx = max(rx, ps[i].second);
            }
        }
        return rx - lx >= 2 * h;
    };
    double lb = 0, ub = 2e5;
    for(int lp = 0; lp < 60; ++lp) {
        const auto mid = (lb + ub) / 2;
        (check(mid) ? lb : ub) = mid;
    }
    return lb * 2;
}

int main() {
    int n;
    while(cin >> n, n) {
        vector<double> x(n), r(n);
        for(int i = 0; i < n; ++i) {
            cin >> x[i] >> r[i];
        }
        cout << fixed << setprecision(10) << solve(move(x), move(r)) << endl;
    }
}