【SPOJ CUIR】圆的面积并
【参考资料】
https://www.cnblogs.com/heisenberg-/p/6740654.html
【前置知识】
【题目见标题】
【题目大意】给 n 个圆,给其圆心何半径,求这 n 个圆覆盖的面积
【算法分析】
两种方法,一种是几何法,一种是自适应辛普森
之前在看自适应辛普森,结果卡时间卡的很不舒服,看底下几何法过的都闪电快如飞,于是决定用几何法
【几何法】
通过计算圆和圆之间的交点,通过交点将圆的面积分为没有覆盖的圆和多次覆盖的多边形
然后将交点加入队列,极角排序后计算
通过对面积的计算,将结果变为仅仅计算一次
【代码】
1 #include<cstdio> 2 #include<cmath> 3 #include<vector> 4 #include<algorithm> 5 #include<map> 6 #include<iostream> 7 #include<complex> 8 #include<queue> 9 #include<stack> 10 using namespace std; 11 const double eps = 1e-9; 12 const double PI = acos(-1.0); 13 const int MAXN = 1010000; 14 int sgn(double x) 15 { 16 if (x < -eps) return -1; 17 if (x > eps) return 1; 18 return 0; 19 } 20 struct V 21 { 22 double x, y; 23 double ang; 24 V(double X = 0, double Y = 0) 25 { 26 //初始化 27 x = X, y = Y; 28 } 29 bool operator ==(const V &b) 30 { 31 return sgn(x - b.x) && sgn(y - b.y); 32 } 33 34 }; 35 V operator +(V a, V b) { return V(a.x + b.x, a.y + b.y); } 36 V operator -(V a, V b) { return V(a.x - b.x, a.y - b.y); } 37 V operator *(V a, double b) { return V(a.x *b, a.y*b); } 38 V operator /(V a, double b) { return V(a.x / b, a.y / b); } 39 //叉积 40 double cross(V a, V b) 41 { 42 return a.x*b.y - a.y*b.x; 43 } 44 typedef V P; 45 //点积 46 struct node 47 { 48 P p; 49 double ang; 50 int id; 51 node(P a, double b, int c) :p(a), ang(b), id(c) {} 52 bool operator<(node a) 53 { 54 return ang < a.ang; 55 } 56 }; 57 double dot(V a, V b) 58 { 59 return a.x*b.x + a.y*b.y; 60 } 61 struct L 62 { 63 P s, t; 64 L() {} 65 L(P a, P b):s(a), t(b) {} 66 }; 67 typedef L S; 68 struct C 69 { 70 P p; 71 double r; 72 bool input() 73 { 74 return scanf("%lf%lf", &p.x, &p.y) != EOF; 75 } 76 bool operator<(const C &o) 77 { 78 if (sgn(r - o.r) != 0) return sgn(r - o.r) == -1; 79 if (sgn(p.x - o.p.x) != 0) return sgn(p.x - o.r) == -1; 80 return sgn(p.y - o.p.y) == -1; 81 } 82 bool operator==(const C &o) 83 { 84 return sgn(r - o.r) == 0 && sgn(p.x - o.p.x)==0 && sgn(p.y - o.p.y)==0; 85 } 86 }; 87 double len(P a) 88 { 89 return sqrt(a.x*a.x + a.y*a.y); 90 } 91 C c[MAXN],arr[MAXN]; 92 int N; 93 P rotate(P a, double cost, double sint) 94 { 95 double x = a.x, y = a.y; 96 return P(x*cost - y * sint, x*sint + y * cost); 97 } 98 pair<P, P> crossP(P a, double r1, P b, double r2) 99 { 100 double d = len(a - b); 101 //计算两圆心的距离 102 double cost = (r1*r1 + d * d - r2 * r2) / (2 * r1 *d); 103 //利用相似求出 104 double sint=(sqrt(1. - cost * cost)); 105 P v = (b - a) / len(b - a) * r1; 106 return make_pair(a + rotate(v, cost,-sint), a + rotate(v, cost, sint)); 107 108 } 109 pair<P, P> crossP(C a, C b) 110 { 111 return crossP(a.p, a.r, b.p, b.r); 112 //方便填写函数的重载 113 } 114 double abs_t(double x) 115 { 116 if (x < -eps) return -x; 117 return x; 118 } 119 double arg(P a) 120 { 121 return arg(complex<double>(a.x, a.y)); 122 } 123 vector<node> vec; 124 double solve() 125 { 126 int n = 0; 127 sort(c+1, c + 1 + N); 128 N = unique(c + 1, c + 1 + N) - (c + 1); 129 for (int i = N; i >= 1; i--) 130 { 131 bool flag = true; 132 for (int j = i + 1; j <= N; j++) 133 { 134 double d = len((c[i].p - c[j].p)); 135 if (sgn(d - abs_t(c[i].r - c[j].r)) <= 0) 136 { 137 flag = false; break; 138 } 139 } 140 if (flag) arr[++n] = c[i]; 141 //去除被包含的圆 142 } 143 double ans = 0; 144 for (int i = 1; i <= n; i++) 145 { 146 vec.clear(); 147 P bound = arr[i].p + P(-arr[i].r, 0); 148 vec.push_back(node(bound, -PI, 0)); 149 vec.push_back(node(bound, PI , 0)); 150 for (int j = 1; j <= n; j++) 151 { 152 if (i == j) continue; 153 double d = len(arr[i].p - arr[j].p); 154 if (sgn(d - (arr[i].r + arr[j].r)) < 0) 155 { 156 pair<P, P> ret = crossP(arr[i], arr[j]); 157 //求取到两个圆的交点 158 double x = arg(ret.first - arr[i].p); 159 //arg函数,求这个交点相对圆心的角度 160 double y = arg(ret.second - arr[i].p); 161 if (sgn(x - y) > 0) 162 { 163 vec.push_back(node(ret.first , x , 1 )); 164 vec.push_back(node(bound , PI, -1)); 165 vec.push_back(node(bound , -PI, 1 )); 166 vec.push_back(node(ret.second, y , -1)); 167 } 168 else 169 { 170 vec.push_back(node(ret.first , x, 1 )); 171 vec.push_back(node(ret.second, y, -1)); 172 } 173 } 174 } 175 sort(vec.begin(), vec.end()); 176 int sum = vec[0].id; 177 for (int j = 1; j <(int)vec.size(); j++) 178 { 179 if (sum==0) 180 { 181 ans += cross(vec[j - 1].p, vec[j].p) / 2.; 182 double x = vec[j - 1].ang; 183 double y = vec[j].ang; 184 double area = arr[i].r*arr[i].r*(y - x) / 2.; 185 P v1 = vec[j - 1].p - arr[i].p; 186 P v2 = vec[j].p - arr[i].p; 187 area -= cross(v1, v2) / 2.; 188 ans += area; 189 } 190 sum += vec[j].id; 191 } 192 } 193 return ans; 194 } 195 int main() 196 { 197 int m; 198 scanf("%d", &m); 199 N = 0; 200 for (int i = 1; i <= m; i++) 201 { 202 C tmp; 203 tmp.input(); 204 scanf("%lf",&tmp.r); 205 if (sgn(tmp.r) != 0) 206 c[++N] = tmp; 207 } 208 printf("%.3f\n",solve()); 209 return 0; 210 }
【自适应辛普森】
利用积分的性质,计算每个圆弧