Problem:
MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]], processEscapes: true }});

凸多角形柱工業都市

凸多角形柱工業都市 (Industrial Convex Pillar City, ICPC) はいくつかの凸多角形柱の形状をした建物からなる街である．あなたはこの街の中を現在地 S から目的地 T に向けて歩こうとしている．今日は日差しが強いので，出来る限りひなたを通らずに目的地へ向かいたい．あなたが立っている地点と太陽を一直線に結んだ線上に建物がある場合，あなたは建物の陰にいるので日差しを受けずにすむ．また，この街の建物の外周には全てひさしが付いているので，建物の外周に沿って歩いている間は，日が当たる縁に沿って歩いても日差しを受けることはない．あなたは建物の内部以外はこの街をどこでも自由に歩くことができる．

もっとも日差しを受けないように現在地から目的地まで歩いた時の，ひなたを歩く距離を出力するプログラムを作成しなさい．

図E1: 第1インプットの場合

図E2: 第2インプットの場合

図E3: 第3インプットの場合

Input

入力は複数のデータセットからなる．
データセットの個数は最大でも 30 個を超えない．
各データセットは次の形式で表される．

N
NV1 H1 X1,1 Y1,1 X1,2 Y1,2 ... X1,NV1 Y1,NV1
...
NVN HN XN,1 YN,1 XN,2 YN,2 ... XN,NVN YN,NVN
θ φ
Sx Sy Tx Ty

１行目の N は建物の数を表す．続く N 行は各建物の形状を指定する．NVi は i 番目の建物を上から見た多角形の頂点数，Hi は i 番目の建物の高さを表す．Xi, j と Yi, j は i 番目の建物を上から見た多角形の j 番目の頂点の x 座標と y 座標を表す．頂点は反時計回りの順で与えられる．全ての建物は上から見ると凸多角形であり，建物の内部に他の建物があったり，頂点や辺が他の多角形と重なったりすることはない．続く行には太陽の方向を表す θ と φ が与えられ，θ は太陽の向きを x の正の方向から反時計回りの角度で表し，φ は地平線からの太陽の仰角，すなわち太陽を見上げた時の視線の方向と地表面がなす角度を表す．ただし，太陽は無限遠にあり，移動中には位置を変えないものとする．続く行には現在地と目的地の座標， (Sx, Sy) と (Tx, Ty) が与えられる．

入力で与えられる数値は全て整数であり，以下の条件を満たす．

1 ≤ N ≤ 100
3 ≤ NVi ≤ 12
1 ≤ Hi ≤ 1,000
0 ≤ θ < 360
0 < φ < 90

座標は全て -1,000 以上 1,000 以下である．
現在地と目的地は異なり，どちらも建物の内部及び外周には存在しない．

入力の終わりは，１つのゼロからなる行で表される．

Output

各データセットについて，ひなたを歩く最短距離を１行に出力せよ．出力には 0.001 を超える絶対誤差があってはならない．

Sample Input

2
4 1 0 0 1 0 1 1 0 1
4 2 2 2 3 2 3 3 2 3
60 45
-1 -1 4 4
4
4 1 0 0 3 1 1 2 0 1
3 2 10 7 8 2 12 4
6 8 7 12 8 13 9 15 10 19 11 24 10 25
5 4 16 2 16 4 12 8 14 2 15 0
167 38
3 3 15 21
12
4 3 -8 -3 -9 -3 -9 -5 -8 -6
4 5 -4 -5 -7 -5 -7 -6 -5 -6
4 2 -4 1 -5 1 -5 -4 -4 -4
4 1 -1 1 -2 1 -2 -4 -1 -3
4 2 2 3 -1 3 -2 2 3 2
4 1 3 1 2 1 2 -3 3 -4
4 7 1 0 0 0 0 -1 1 -1
4 4 9 5 7 5 7 4 10 4
4 3 6 5 5 4 5 0 6 0
4 5 8 -1 5 -1 6 -2 8 -2
4 1 10 0 9 0 9 -2 10 -1
4 6 10 2 8 2 8 1 10 1
131 78
-10 10 10 -10
0

Output for Sample Input

1.93185
7.87174
20.86840

Solution in Rust (judge verdict: Accepted)

use std::cmp;
use std::cmp::max;

fn main() {
    let (r, w) = (std::io::stdin(), std::io::stdout());
    let mut sc = IO::new(r.lock(), w.lock());
    loop {
        let n: usize = sc.read();
        if n == 0 {
            return;
        }

        let mut buildings = vec![];
        for _ in 0..n {
            let v: usize = sc.read();
            let h: f64 = sc.read();
            let v: Vec<(f64, f64)> = (0..v).map(|_| (sc.read(), sc.read())).collect();
            buildings.push((h, v));
        }

        let theta: f64 = sc.read::<f64>().to_radians();
        let phi: f64 = sc.read::<f64>().to_radians();
        let mut points = vec![];
        for (h, mut v) in buildings.into_iter() {
            let r = h / phi.tan();
            let n = v.len();
            for i in 0..n {
                let (x, y) = v[i];
                let dy = -theta.sin() * r;
                let dx = -theta.cos() * r;
                let nx = x + dx;
                let ny = y + dy;
                v.push((nx, ny));
            }

            let p = v
                .into_iter()
                .map(|(x, y)| Point { x, y })
                .collect::<Vec<_>>();
            let i = extract_convex_hull(&p, false);
            let p = i.into_iter().map(|i| p[i]).collect::<Vec<_>>();
            points.push(p);
        }

        let sx: f64 = sc.read();
        let sy: f64 = sc.read();
        let tx: f64 = sc.read();
        let ty: f64 = sc.read();

        points.push(vec![Point { x: sx, y: sy }]);
        points.push(vec![Point { x: tx, y: ty }]);

        let n = points.len();
        let mut dist = vec![vec![1e18; n]; n];
        for i in 0..n {
            dist[i][i] = 0.0;
            for j in 0..i {
                dist[i][j] = b2b_d2(&points[i], &points[j]).sqrt();
                dist[j][i] = dist[i][j];
            }
        }

        for k in 0..n {
            for i in 0..n {
                for j in 0..n {
                    dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
                }
            }
        }

        println!("{}", dist[n - 2][n - 1]);
    }
}

fn min<T: PartialOrd>(a: T, b: T) -> T {
    if a > b {
        b
    } else {
        a
    }
}

fn b2b_d2(b1: &[Point], b2: &[Point]) -> f64 {
    let mut min_d = 1e18;

    for b1i in 0..b1.len() {
        let p0 = b1[b1i];
        for b2i in 0..b2.len() {
            let p1 = b2[b2i];
            let p2 = b2[(b2i + 1) % b2.len()];
            min_d = min(min_d, min_d2(&p0, &p1, &p2));
        }

        if is_included(&p0, b2) {
            return 0.0;
        }
    }
    for b2i in 0..b2.len() {
        let p0 = b2[b2i];
        for b1i in 0..b1.len() {
            let p1 = b1[b1i];
            let p2 = b1[(b1i + 1) % b1.len()];
            min_d = min(min_d, min_d2(&p0, &p1, &p2));
        }

        if is_included(&p0, b1) {
            return 0.0;
        }
    }

    for i in 0..b1.len() {
        for j in 0..b2.len() {
            let dx = b1[i].x - b2[j].x;
            let dy = b1[i].y - b2[j].y;
            min_d = min(min_d, dx * dx + dy * dy);
        }
    }
    min_d
}

fn is_included(p: &Point, convex: &[Point]) -> bool {
    let mut convex = convex.to_vec();
    convex.push(p.clone());
    let c = extract_convex_hull(&convex, false);
    if c.contains(&(convex.len() - 1)) {
        false
    } else {
        true
    }
}

fn min_d2(p0: &Point, p1: &Point, p2: &Point) -> f64 {
    let x0 = p0.x;
    let y0 = p0.y;
    let x1 = p1.x;
    let y1 = p1.y;
    let x2 = p2.x;
    let y2 = p2.y;
    let a = x2 - x1;
    let b = y2 - y1;
    let a2 = a * a;
    let b2 = b * b;
    let r2 = a2 + b2;
    let tt = -(a * (x1 - x0) + b * (y1 - y0));
    if tt < 0.0 {
        return (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
    }
    if tt > r2 {
        return (x2 - x0) * (x2 - x0) + (y2 - y0) * (y2 - y0);
    }
    let f1 = a * (y1 - y0) - b * (x1 - x0);
    return (f1 * f1) / r2;
}

pub fn extract_convex_hull(points: &[Point], contain_on_segment: bool) -> Vec<usize> {
    let n = points.len();
    if n <= 1 {
        return vec![0];
    }

    let mut ps: Vec<usize> = (0..n).collect();
    ps.sort_by(|&a, &b| {
        if (points[a].x - points[b].x).abs() < std::f64::EPSILON {
            points[a].y.partial_cmp(&points[b].y).unwrap()
        } else {
            points[a].x.partial_cmp(&points[b].x).unwrap()
        }
    });

    let mut qs: Vec<usize> = Vec::new();
    for &i in &ps {
        while qs.len() > 1 {
            let k = qs.len();
            let det = (points[qs[k - 1]] - points[qs[k - 2]]).det(&(points[i] - points[qs[k - 1]]));
            if det < 0.0 || (det <= 0.0 && !contain_on_segment) {
                qs.pop();
            } else {
                break;
            }
        }
        qs.push(i);
    }

    let t = qs.len();
    for i in (0..(n - 1)).rev() {
        let i = ps[i];
        while qs.len() > t {
            let k = qs.len();
            let det = (points[qs[k - 1]] - points[qs[k - 2]]).det(&(points[i] - points[qs[k - 1]]));
            if det < 0.0 || (det <= 0.0 && !contain_on_segment) {
                qs.pop();
            } else {
                break;
            }
        }
        qs.push(i);
    }

    qs.pop();
    qs
}

#[derive(Debug, Copy, Clone)]
pub struct Point {
    x: f64,
    y: f64,
}

impl std::ops::Sub for Point {
    type Output = Point;
    fn sub(self, other: Point) -> Point {
        Point {
            x: self.x - other.x,
            y: self.y - other.y,
        }
    }
}

impl Point {
    pub fn det(&self, other: &Point) -> f64 {
        self.x * other.y - self.y * other.x
    }
}
pub struct IO<R, W: std::io::Write>(R, std::io::BufWriter<W>);

impl<R: std::io::Read, W: std::io::Write> IO<R, W> {
    pub fn new(r: R, w: W) -> Self {
        IO(r, std::io::BufWriter::new(w))
    }
    pub fn write<S: ToString>(&mut self, s: S) {
        use std::io::Write;
        self.1.write_all(s.to_string().as_bytes()).unwrap();
    }
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .0
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n' || b == b'\r' || b == b'\t')
            .take_while(|&b| b != b' ' && b != b'\n' && b != b'\r' && b != b'\t')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}