AOJ 2092 Pythagoraslope

今までで一番時間をかけた問題.

基底変換や,線分と放物線の交点等が入り混じった実装問題(not強実装).

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;
import static java.lang.Math.*;
import static java.util.Arrays.*;
import static java.util.Collections.*;

// Pythagoraslope
// 2012/12/13
public class Main{
	Scanner sc=new Scanner(System.in);

	int INF=1<<28;
	double EPS=1e-4;

	int n;
	double g;
	double x, y;
	double[] xs1, ys1, xs2, ys2;

	void run(){
		for(;;){
			n=sc.nextInt();
			if(n==0){
				break;
			}
			g=sc.nextDouble();
			x=sc.nextDouble();
			y=sc.nextDouble();
			xs1=new double[n];
			ys1=new double[n];
			xs2=new double[n];
			ys2=new double[n];
			for(int i=0; i<n; i++){
				double x1=sc.nextDouble();
				double y1=sc.nextDouble();
				double x2=sc.nextDouble();
				double y2=sc.nextDouble();
				if(x1>x2){
					xs1[i]=x1;
					ys1[i]=y1;
					xs2[i]=x2;
					ys2[i]=y2;
				}else{
					xs1[i]=x2;
					ys1[i]=y2;
					xs2[i]=x1;
					ys2[i]=y1;
				}
			}
			solve();
		}
	}

	P p, v, a;
	int pseg, seg;
	boolean finished;
	double ans;

	void solve(){
		p=new P(x, y);
		v=new P(0, 0);
		a=new P(0, -g);
		pseg=-1;
		finished=false;
		boolean first=true;

		ans=Double.MAX_VALUE;
		for(;;){
			if(first){
				fallFirst();
				first=false;
			}else{
				fall();
			}
			if(finished){
				break;
			}
			slope();
		}
		println(String.format("%.12f", ans));
	}

	void fallFirst(){
		double tMin=Double.MAX_VALUE;
		seg=-1;
		for(int i=0; i<n; i++){
			double x1=xs1[i], y1=ys1[i], x2=xs2[i], y2=ys2[i];
			double a=(y2-y1)/(x2-x1);
			double b=(x2*y1-x1*y2)/(x2-x1);
			double y=a*p.x+b;
			for(double t : quad(-g/2, 0, p.y-y)){
				if(t>EPS&&between(p.x, x1, x2)&&between(y, y1, y2)){
					if(t<tMin){
						tMin=t;
						seg=i;
					}
				}
			}
		}

		if(seg==-1){
			ans=p.x;
			finished=true;
		}else{
			p=p.add(a.div(2).mul(sq(tMin))).add(v.mul(tMin));
			v=v.add(a.mul(tMin));
		}
		pseg=seg;
	}

	void fall(){
		double tMin=Double.MAX_VALUE;
		seg=-1;
		for(int i=0; i<n; i++){
			double x1=xs1[i], y1=ys1[i], x2=xs2[i], y2=ys2[i];
			double a=(y2-y1)/(x2-x1);
			double b=(x2*y1-x1*y2)/(x2-x1);
			for(double t : quad(-g/2, v.y-a*v.x, p.y-a*p.x-b)){
				double x=v.x*t+p.x;
				double y=-g/2*t*t+v.y*t+p.y;
				if(t>EPS&&between(x, x1, x2)&&between(y, y1, y2)){
					if(t<tMin/* &&i!=pseg */){
						tMin=t;
						seg=i;
					}
				}
			}
		}
		if(seg==-1){
			for(double t : quad(-g/2, v.y, p.y)){
				double x=v.x*t+p.x;
				if(t>EPS){
					ans=x;
				}
			}
			double[] ts=quad(-g/2, v.y, p.y);
			ans=v.x*max(ts[0], ts[1])+p.x;
			finished=true;
		}else{
			p=p.add(a.div(2).mul(sq(tMin))).add(v.mul(tMin));
			v=v.add(a.mul(tMin));
		}
		pseg=seg;
	}

	void slope(){
		P p1=new P(xs1[seg], ys1[seg]);
		P p2=new P(xs2[seg], ys2[seg]);

		P ex=p2.sub(p1);
		ex=ex.div(ex.abs());
		P ey=ex.rot90();

		P q1=inv(ex, ey, p1);
		P q2=inv(ex, ey, p2);
		P q=inv(ex, ey, p);
		P u=inv(ex, ey, v);
		P b=inv(ex, ey, a);

		if(u.y+EPS>0){
			u.y=b.y=0;
		}else{
			u.y=b.y=0;
			double tMin=Double.MAX_VALUE;
			for(P qq : new P[]{q1, q2}){
				double[] ts=quad(b.x/2, u.x, q.x-qq.x);
				for(double t : ts){
					if(t>EPS){
						tMin=min(tMin, t);
					}
				}
			}
			q=q.add(b.div(2).mul(sq(tMin))).add(u.mul(tMin));
			u=u.add(b.mul(tMin));
		}
		p=mul(ex, ey, q);
		v=mul(ex, ey, u);
	}

	double sq(double x){
		return x*x;
	}

	// b=[A1 A2]x
	P mul(P A1, P A2, P x){
		P b=new P(0, 0);
		b.x=A1.x*x.x+A2.x*x.y;
		b.y=A1.y*x.x+A2.y*x.y;
		return b;
	}

	// [A1 A2]x = b
	P inv(P A1, P A2, P b){
		P x=new P(0, 0);
		x.x=A2.y*b.x-A2.x*b.y;
		x.y=-A1.y*b.x+A1.x*b.y;
		return x.mul(-1);
	}

	boolean between(double val, double a, double b){
		return min(a, b)+EPS<val&&val+EPS<max(a, b);
	}

	// y=ax+b
	// y=cx^2+dx+e
	P[] intersection(double a, double b, double c, double d, double e){
		double[] xs=quad(c, d-a, e-b);
		P[] ps=new P[xs.length];
		for(int i=0; i<xs.length; i++){
			ps[i]=new P(xs[i], a*xs[i]+b);
		}
		return ps;
	}

	double[] quad(double a, double b, double c){
		double D=b*b-4*a*c;
		if(D<0){
			return new double[0];
		}else{
			double E=1e-6;
			double sD=sqrt(max(D, 0));
			double x1, x2;
			if(abs(-b+sD)<E){
				x1=(2*c)/(-b-sD);
			}else{
				x1=(-b+sD)/(2*a);
			}
			if(abs(-b-sD)<E){
				x2=(2*c)/(-b+sD);
			}else{
				x2=(-b-sD)/(2*a);
			}
			return new double[]{x1, x2};
		}
	}

	class P{
		double x, y;

		P(double x, double y){
			this.x=x;
			this.y=y;
		}

		P add(P p){
			return new P(x+p.x, y+p.y);
		}

		P sub(P p){
			return new P(x-p.x, y-p.y);
		}

		P mul(double m){
			return new P(x*m, y*m);
		}

		P div(double d){
			return new P(x/d, y/d);
		}

		double abs(){
			return sqrt(abs2());
		}

		double abs2(){
			return x*x+y*y;
		}

		double arg(){
			return atan2(y, x);
		}

		double dot(P p){
			return x*p.x+y*p.y;
		}

		double det(P p){
			return x*p.y-y*p.x;
		}

		double ang(P p){
			return atan2(det(p), dot(p));
		}

		P rot90(){
			return new P(y, -x);
		}

		P rot(double d){
			return new P(cos(d)*x-sin(d)*y, sin(d)*x+cos(d)*y);
		}

		public String toString(){
			return "("+x+","+y+")";
		}
	}

	void println(String s){
		System.out.println(s);
	}

	public static void main(String[] args){
		new Main().run();
	}
}