// Intended O(N^2 log N + NQ) solution. import java.util.*; import java.io.*; public class orchard_font { public static void main(String[] args) throws Exception { PrintWriter out = new PrintWriter(System.out); new orchard_font(new FastScanner(System.in), out); out.close(); } // Run a horizontal sweep (x-axis) with the vertical strip [y0, y1]. Strip run_horizontal_sweep(double y0, double y1, int num_trees, circle2[] apple_trees) { ArrayList events = new ArrayList<>(); double y_mid = (y1+y0) * 0.5; for (int i=0; i ribbons = new ArrayList<>(); HoriEvent left_bracket = null; int num_on_stack = 0; for (HoriEvent e : events) { if (e.state == 0) { if (num_on_stack == 0) left_bracket = e; num_on_stack++; } else { num_on_stack--; if (num_on_stack == 0) { double x0 = Math.min(left_bracket.tree.calcX(y0, -1), left_bracket.tree.calcX(y1, -1)); double x1 = Math.max(e.tree.calcX(y0, 1), e.tree.calcX(y1, 1)); ribbons.add(new Ribbon(x0, x1, y0, y1, left_bracket.tree, e.tree)); } } } return new Strip(y0, y1, ribbons); } // Creates events for intersection points. // However, we only add intersection points that are not contained within // some other circle. There can be at most O(n) "boundary" intersection points. void run_radial_sweep(int i, int num_trees, circle2[] apple_trees, ArrayList vert_events) { // Create events for all other circles sweeping around i. int num_on_stack = 0; ArrayList events = new ArrayList<>(); for (int j=0; j events = new ArrayList<>(); for (int i=0; i strips = new ArrayList<>(); Event last_event = null; Collections.sort(events); for (Event e : events) { if (last_event != null && last_event.y < e.y - GEOM.EPS) { Strip result = run_horizontal_sweep(last_event.y, e.y, num_trees, apple_trees); if (result.ribbons.length > 0) strips.add(result); } last_event = e; } return strips.toArray(new Strip[0]); } public orchard_font(FastScanner in, PrintWriter out) { int num_trees = in.nextInt(); int num_queries = in.nextInt(); circle2[] apple_trees = new circle2[num_trees]; for (int i=0; i= 0 && y0+h + GEOM.EPS < strips[high_i].y0) high_i--; double area = 0; if (low_i <= high_i) { for (Ribbon r : strips[low_i].ribbons) area += r.calcArea(x0, y0, w, h); if (low_i < high_i) for (Ribbon r : strips[high_i].ribbons) area += r.calcArea(x0, y0, w, h); for (int i=low_i+1; i { vec2 pt; double angle; // 0 - start interval // 1 - end interval int state; ArcEvent(double angle, int state, vec2 pt) { this.angle = angle; this.state = state; this.pt = pt; } public int compareTo(ArcEvent rhs) { return Double.compare(angle, rhs.angle); } } class HoriEvent implements Comparable { double x; // 0 - start circle // 1 - end circle int state; circle2 tree; HoriEvent(double x, int state, circle2 tree) { this.x = x; this.state = state; this.tree = tree; } public int compareTo(HoriEvent rhs) { return Double.compare(x, rhs.x); } } class Event implements Comparable { double y; // 0 - start circle // 1 - end circle // 2 - circle intersections int state; int circle_id; Event(double y, int state, int circle_id) { this.y = y; this.state = state; this.circle_id = circle_id; } public int compareTo(Event rhs) { return Double.compare(y, rhs.y); } } // This class represents a disjoint row of "Ribbons". class Strip { double y0, y1; Ribbon[] ribbons; double[] prefix_sum; Strip(double y0, double y1, ArrayList ribbons) { this.y0 = y0; this.y1 = y1; this.ribbons = ribbons.toArray(new Ribbon[0]); // Compute the prefix sum of areas. prefix_sum = new double[this.ribbons.length+1]; for (int i=0; i crits) { double y; if (left.cen.x-left.rad <= line_x + GEOM.EPS && line_x + GEOM.EPS <= left.cen.x) { y = left.calcY(line_x, -1); if (y0 < y && y < y1) crits.add(y); y = left.calcY(line_x, 1); if (y0 < y && y < y1) crits.add(y); } if (right.cen.x <= line_x + GEOM.EPS && line_x + GEOM.EPS <= right.cen.x+right.rad) { y = right.calcY(line_x, -1); if (y0 < y && y < y1) crits.add(y); y = right.calcY(line_x, 1); if (y0 < y && y < y1) crits.add(y); } } double calcArea(double box_x, double box_y, double w, double h) { ArrayList critical_ys = new ArrayList<>(); critical_ys.add(y1); // For each vertical line add_crits_for_line(box_x, critical_ys); add_crits_for_line(box_x+w, critical_ys); // Create critical points for horizontal lines if (y0 < box_y && box_y < y1) critical_ys.add(box_y); if (y0 < box_y+h && box_y+h < y1) critical_ys.add(box_y+h); Collections.sort(critical_ys); double total_area = 0; double last_y = Math.max(y0, box_y); for (double y : critical_ys) { if (y > box_y+h) break; if (last_y + 1e-9 < y) { circle2 curve_left = left; circle2 curve_right = right; vec2 bottom_left, top_left, bottom_right, top_right; bottom_left = new vec2(left.calcX(last_y, -1), last_y); top_left = new vec2(left.calcX(y, -1), y); bottom_right = new vec2(right.calcX(last_y, 1), last_y); top_right = new vec2(right.calcX(y, 1), y); double mid_y = 0.5 * (last_y + y); // Is the left side a curve or a vertical line? double left_mid_x = left.calcX(mid_y, -1); if (left_mid_x <= box_x) { bottom_left = new vec2(box_x, last_y); top_left = new vec2(box_x, y); curve_left = null; left_mid_x = box_x; } // Is the right side a curve or a vertical line? double right_mid_x = right.calcX(mid_y, 1); if (box_x+w <= right_mid_x) { bottom_right = new vec2(box_x+w, last_y); top_right = new vec2(box_x+w, y); curve_right = null; right_mid_x = box_x+w; } double mid_x = 0.5 * (left_mid_x + right_mid_x); if (box_x < mid_x && mid_x < box_x + w && box_y < mid_y && mid_y < box_y + h) { double lense1 = 0; double lense2 = 0; double tri1 = triArea(bottom_left, top_left, bottom_right); double tri2 = triArea(top_left, bottom_right, top_right); if (curve_left != null) lense1 = calcLense(bottom_left, top_left, left); if (curve_right != null) lense2 = calcLense(bottom_right, top_right, right); double area = lense1 + lense2 + tri1 + tri2; total_area += area; } last_y = y; } } return total_area; } } class circle2 { vec2 cen; double rad; circle2(vec2 c, double r) { cen=c; rad=r; } boolean eq(circle2 rhs) { return cen.eq(rhs.cen) && GEOM.eq(rad, rhs.rad); } vec2[] intersect(circle2 rhs) { if (eq(rhs)) return new vec2[0]; double L = rad; double R = rhs.rad; double B = cen.dist(rhs.cen); int nSols = GEOM.testTriangle(L,R,B); if(nSols == 0) return new vec2[0]; double c = (B*B+L*L-R*R)/(2*B); double b = Math.sqrt(Math.abs(L*L-c*c)); vec2 u = rhs.cen.sub(cen).normalize(); vec2 v = new vec2(-u.y,u.x); vec2 vc = u.scale(c), vb = v.scale(b); if(nSols==1) return new vec2[]{cen.add(vc).add(vb)}; return new vec2[]{cen.add(vc).sub(vb),cen.add(vc).add(vb)}; } // side -> -1 or 1 depending on opening or closing arc. double calcX(double y, int side) { double delta = Math.abs(cen.y - y); if (delta > rad) { System.out.printf("BADNESS in calc_x %.6f %.6f%n", delta, rad); System.out.printf("%.6f %.6f%n", cen.y, y); } return cen.x + side * Math.sqrt(rad * rad - delta * delta); } double calcY(double x, int side) { double delta = Math.abs(cen.x - x); if (delta > rad) System.out.println("BADNESS in calc_y"); return cen.y + side * Math.sqrt(rad * rad - delta * delta); } public String toString() { return String.format("<%.6f, %.6f> %.6f", cen.x, cen.y, rad); } } class vec2 { double x,y; vec2(double a, double b) { x=a;y=b; } boolean eq(vec2 rhs) {return GEOM.eq(x, rhs.x) && GEOM.eq(y, rhs.y);} vec2 add(vec2 rhs) {return new vec2(x+rhs.x,y+rhs.y);} vec2 sub(vec2 rhs) {return new vec2(x-rhs.x,y-rhs.y);} vec2 scale(double s) {return new vec2(x*s,y*s);} double dot(vec2 rhs) {return x*rhs.x + y*rhs.y;} double cross(vec2 rhs) {return x*rhs.y - y*rhs.x;} double mag() {return Math.sqrt(x*x+y*y);} double mag2() {return x*x+y*y;} double dist(vec2 rhs) {return sub(rhs).mag();} double dist2(vec2 rhs) {return sub(rhs).mag2();} vec2 normalize() {return scale(1./mag());} //assumes mag()!=0 double angle() {return Math.atan2(y, x);} double angleBtwn(vec2 rhs) {return Math.atan2(cross(rhs), dot(rhs));} double triArea(vec2 rhs) {return Math.abs(cross(rhs)) * 0.5;} public String toString() { return String.format("<%.6f, %.6f>", x, y); } } class GEOM { static double EPS = 1e-9; static boolean eq(double a, double b) { double d = Math.abs(a-b); if (d < EPS) return true; return d < EPS*Math.max(Math.abs(a), Math.abs(b)); } static int testTriangle(double a, double b, double c) { double[] tri = new double[]{a,b,c}; Arrays.sort(tri); double T = tri[0]+tri[1]; if(GEOM.eq(T, tri[2])) return 1; //degenerate return T>tri[2]?2:0; // ok, not ok } } class FastScanner{ private InputStream stream; private byte[] buf = new byte[1024]; private int curChar; private int numChars; public FastScanner(InputStream stream) { this.stream = stream; } int read() { if (numChars == -1) throw new InputMismatchException(); if (curChar >= numChars){ curChar = 0; try{ numChars = stream.read(buf); } catch (IOException e) { throw new InputMismatchException(); } if (numChars <= 0) return -1; } return buf[curChar++]; } boolean isSpaceChar(int c) { return c==' '||c=='\n'||c=='\r'||c=='\t'||c==-1; } boolean isEndline(int c) { return c=='\n'||c=='\r'||c==-1; } int nextInt() { return Integer.parseInt(next()); } long nextLong() { return Long.parseLong(next()); } double nextDouble() { return Double.parseDouble(next()); } String next(){ int c = read(); while (isSpaceChar(c)) c = read(); StringBuilder res = new StringBuilder(); do{ res.appendCodePoint(c); c = read(); }while(!isSpaceChar(c)); return res.toString(); } String nextLine(){ int c = read(); while (isEndline(c)) c = read(); StringBuilder res = new StringBuilder(); do{ res.appendCodePoint(c); c = read(); }while(!isEndline(c)); return res.toString(); } }