HDU 3467 Song of the Siren(圆交)
Problem Description
In the unimaginable popular DotA game, a hero Naga Siren, also known as Slithice, has a very powerful skill: Song of the Siren, Slithice’s charming song draws all of the nearby enemies into deep sleep.
![](http://acm.hdu.edu.cn/data/images/C283-1008-1.jpg)
Now iSea meet a group of very powerful opponent, he needs to use this skill to draw all of the enemy's five heroes into hypnosis, and only finishing this leaves the chance to win for him. If we have already got the coordinates of the opponents, in where can Slithice sing the song to hypnotize all the opponents?
![](http://acm.hdu.edu.cn/data/images/C283-1008-1.jpg)
Now iSea meet a group of very powerful opponent, he needs to use this skill to draw all of the enemy's five heroes into hypnosis, and only finishing this leaves the chance to win for him. If we have already got the coordinates of the opponents, in where can Slithice sing the song to hypnotize all the opponents?
Input
There are several test cases in the input.
Each test case begin with an integer R (1 ≤ R ≤ 1000), indicating the range of the song, all heroes are hypnotized if the distance between Slithice is no larger than R.
The following line contains ten integers, indicating the coordinates of the five opponents, and -10000 ≤ x, y ≤ 10000.
The input terminates by end of file marker.
Each test case begin with an integer R (1 ≤ R ≤ 1000), indicating the range of the song, all heroes are hypnotized if the distance between Slithice is no larger than R.
The following line contains ten integers, indicating the coordinates of the five opponents, and -10000 ≤ x, y ≤ 10000.
The input terminates by end of file marker.
Output
For each test case, output one line:
If no such point, output "Poor iSea, maybe 2012 is coming!"
If only one such point, output "Only the point (x, y) is for victory." (x, y) that the only point Slithice can sing.
If there are plenty of such points, output "The total possible area is X." X indicating the total area Slithice can sing.
All the floating numbers should be rounded to two fractional digits.
If no such point, output "Poor iSea, maybe 2012 is coming!"
If only one such point, output "Only the point (x, y) is for victory." (x, y) that the only point Slithice can sing.
If there are plenty of such points, output "The total possible area is X." X indicating the total area Slithice can sing.
All the floating numbers should be rounded to two fractional digits.
题目大意:给5个点,问是否存在一个点,使得这个点在半径R内包含这5个点,若只存在一个点则输出这个点,存在多个则输出这个点集的面积。
据说此题可以各种水……
关于集合求交的部分,本人选择了一边求一个新的集合一边和旧的集合合并的方法(都先存起来最后合并的话不知道怎么分辨哪些是同一个集合的,不知道有木有什么高端的姿势?),不过集合的数量最多应该不会超过2(单从代码上看是这样)。
复杂度O(n^2)
代码(15MS):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <cmath> 7 using namespace std; 8 9 const double PI = acos(-1.0); 10 const double EPS = 1e-8; 11 12 double Deg2Rad(double deg){return (deg * PI / 180.0);} 13 double Rad2Deg(double rad){return (rad * 180.0 / PI);} 14 double Sin(double deg){return sin(Deg2Rad(deg));} 15 double Cos(double deg){return cos(Deg2Rad(deg));} 16 double ArcSin(double val){return Rad2Deg(asin(val));} 17 double ArcCos(double val){return Rad2Deg(acos(val));} 18 double Sqrt(double val){return sqrt(val);} 19 20 inline int sgn(double x) { 21 return (x > EPS) - (x < -EPS); 22 } 23 24 inline double sqr(double x) { 25 return x * x; 26 } 27 28 struct Point { 29 double x, y; 30 Point() {} 31 Point(double x, double y): x(x), y(y) {} 32 void read() { 33 scanf("%lf%lf", &x, &y); 34 } 35 double length() const { 36 return sqrt(x * x + y * y); 37 } 38 Point operator + (const Point &rhs) const { 39 return Point(x + rhs.x, y + rhs.y); 40 } 41 Point operator - (const Point &rhs) const { 42 return Point(x - rhs.x, y - rhs.y); 43 } 44 Point operator * (double t) const { 45 return Point(x * t, y * t); 46 } 47 Point operator / (double t) const { 48 return Point(x / t, y / t); 49 } 50 Point unit() const { 51 double l = length(); 52 return *this / l; 53 } 54 double angle() const { 55 return atan2(y, x); 56 } 57 }; 58 59 double dist(const Point &a, const Point &b) { 60 return (a - b).length(); 61 } 62 63 Point rotate(const Point &p, double angle, const Point &o = Point(0, 0)) { 64 Point t = p - o; 65 double x = t.x * cos(angle) - t.y * sin(angle); 66 double y = t.y * cos(angle) + t.x * sin(angle); 67 return Point(x, y) + o; 68 } 69 70 double cross(const Point &a, const Point &b) { 71 return a.x * b.y - a.y * b.x; 72 } 73 74 double cross(const Point &sp, const Point &ep, const Point &op) { 75 return cross(sp - op, ep - op); 76 } 77 78 struct Region { 79 double st, ed; 80 Region() {} 81 Region(double st, double ed): st(st), ed(ed) {} 82 }; 83 84 struct Circle { 85 Point c; 86 double r; 87 Circle() {} 88 Circle(Point c, double r): c(c), r(r) {} 89 void read() { 90 c.read(); 91 scanf("%lf", &r); 92 } 93 double area() const { 94 return PI * r * r; 95 } 96 bool contain(const Circle &rhs) const { 97 return sgn(dist(c, rhs.c) + rhs.r - r) <= 0; 98 } 99 bool contain(const Point &p) const { 100 return sgn(dist(c, p) - r) <= 0; 101 } 102 bool intersect(const Circle &rhs) const { 103 return sgn(dist(c, rhs.c) - r - rhs.r) < 0; 104 } 105 bool tangency(const Circle &rhs) const { 106 return sgn(dist(c, rhs.c) - r - rhs.r) == 0; 107 } 108 Point pos(double angle) const { 109 Point p = Point(c.x + r, c.y); 110 return rotate(p, angle, c); 111 } 112 }; 113 114 void intersection(const Circle &cir1, const Circle &cir2, Point &p1, Point &p2) { 115 double l = dist(cir1.c, cir2.c); 116 double d = (sqr(l) - sqr(cir2.r) + sqr(cir1.r)) / (2 * l); 117 double d2 = sqrt(sqr(cir1.r) - sqr(d)); 118 Point mid = cir1.c + (cir2.c - cir1.c).unit() * d; 119 Point v = rotate(cir2.c - cir1.c, PI / 2).unit() * d2; 120 p1 = mid + v, p2 = mid - v; 121 } 122 123 const int MAXN = 10; 124 125 126 struct Region_vector { 127 int n; 128 Region v[5]; 129 void clear() { 130 n = 0; 131 } 132 void add(const Region &r) { 133 v[n++] = r; 134 } 135 } *last, *cur; 136 137 Circle cir[MAXN]; 138 bool del[MAXN]; 139 double r; 140 int n = 5; 141 142 double CommonArea(const Circle &A, const Circle &B) { 143 double area = 0.0; 144 const Circle & M = (A.r > B.r) ? A : B; 145 const Circle & N = (A.r > B.r) ? B : A; 146 double D = dist(M.c, N.c); 147 if((D < M.r + N.r) && (D > M.r - N.r)) { 148 double cosM = (M.r * M.r + D * D - N.r * N.r) / (2.0 * M.r * D); 149 double cosN = (N.r * N.r + D * D - M.r * M.r) / (2.0 * N.r * D); 150 double alpha = 2.0 * ArcCos(cosM); 151 double beta = 2.0 * ArcCos(cosN); 152 double TM = 0.5 * M.r * M.r * Sin(alpha); 153 double TN = 0.5 * N.r * N.r * Sin(beta); 154 double FM = (alpha / 360.0) * M.area(); 155 double FN = (beta / 360.0) * N.area(); 156 area = FM + FN - TM - TN; 157 } 158 else if(D <= M.r - N.r) { 159 area = N.area(); 160 } 161 return area; 162 } 163 164 bool isOnlyOnePoint() { 165 bool flag = false; 166 Point t; 167 for(int i = 0; i < n; ++i) 168 for(int j = i + 1; j < n; ++j) { 169 if(cir[i].tangency(cir[j])) { 170 flag = true; 171 t = (cir[i].c + cir[j].c) / 2; 172 break; 173 } 174 } 175 if(!flag) return false; 176 for(int i = 0; i < n; ++i) 177 if(!cir[i].contain(t)) return false; 178 printf("Only the point (%.2f, %.2f) is for victory.\n", t.x + EPS, t.y + EPS); 179 return true; 180 } 181 182 bool solve() { 183 if(isOnlyOnePoint()) return true; 184 memset(del, 0, sizeof(del)); 185 for(int i = 0; i < n; ++i) 186 for(int j = 0; j < n; ++j) { 187 if(del[j] || i == j) continue; 188 if(cir[i].contain(cir[j])) { 189 del[i] = true; 190 break; 191 } 192 } 193 double ans = 0; 194 for(int i = 0; i < n; ++i) { 195 if(del[i]) continue; 196 last->clear(); 197 Point p1, p2; 198 for(int j = 0; j < n; ++j) { 199 if(del[j] || i == j) continue; 200 if(!cir[i].intersect(cir[j])) return false; 201 cur->clear(); 202 intersection(cir[i], cir[j], p1, p2); 203 double rs = (p2 - cir[i].c).angle(), rt = (p1 - cir[i].c).angle(); 204 if(sgn(rs) < 0) rs += 2 * PI; 205 if(sgn(rt) < 0) rt += 2 * PI; 206 if(last->n == 0) { 207 if(sgn(rt - rs) < 0) cur->add(Region(rs, 2 * PI)), cur->add(Region(0, rt)); 208 else cur->add(Region(rs, rt)); 209 } else { 210 for(int k = 0; k < last->n; ++k) { 211 if(sgn(rt - rs) < 0) { 212 if(sgn(last->v[k].st - rt) >= 0 && sgn(rs - last->v[k].ed) >= 0) continue; 213 if(sgn(last->v[k].st - rt) < 0) cur->add(Region(last->v[k].st, min(last->v[k].ed, rt))); 214 if(sgn(rs - last->v[k].ed) < 0) cur->add(Region(max(last->v[k].st, rs), last->v[k].ed)); 215 } else { 216 if(sgn(rt - last->v[k].st) <= 0 || sgn(last->v[k].ed - rs) <= 0) continue; 217 cur->add(Region(max(rs, last->v[k].st), min(rt, last->v[k].ed))); 218 } 219 } 220 } 221 swap(last, cur); 222 if(last->n == 0) break; 223 } 224 for(int j = 0; j < last->n; ++j) { 225 p1 = cir[i].pos(last->v[j].st); 226 p2 = cir[i].pos(last->v[j].ed); 227 ans += cross(p1, p2) / 2; 228 double angle = last->v[j].ed - last->v[j].st; 229 ans += 0.5 * sqr(cir[i].r) * (angle - sin(angle)); 230 } 231 } 232 if(sgn(ans) == 0) return false; 233 printf("The total possible area is %.2f.\n", ans + EPS); 234 //printf("%.2f\n", CommonArea(cir[0], cir[4])); 235 return true; 236 } 237 238 int main() { 239 last = new Region_vector, cur = new Region_vector; 240 while(scanf("%lf", &r) != EOF) { 241 Point t; 242 for(int i = 0; i < n; ++i) { 243 t.read(); 244 cir[i] = Circle(t, r); 245 } 246 if(!solve()) puts("Poor iSea, maybe 2012 is coming!"); 247 } 248 }
以下转自:http://hi.baidu.com/aekdycoin/item/7618bee9f473ed3e86d9ded6
【问题求解】
给定N 个圆形,求出其交集.
【算法分析】
![](//images0.cnblogs.com/blog2015/535397/201504/090017578839044.jpg)
考虑上图中的蓝色圆,绿色的圆和蓝色的圆交于 A,B 2个交点 ,我们在逆时针系下考虑,那么 可以知道 对于蓝色的圆,它对应的某个 角度区间被覆盖了
假设 区间为 [A, B], 并且角度是按照 圆心到交点的 向量的 极角来定义 (为了方便,我一般都把角度转化到 [0,2pi]区间内) 那么可以知道在这种 标识情况下,可能存在以下情况
给定N 个圆形,求出其交集.
【算法分析】
![](http://images0.cnblogs.com/blog2015/535397/201504/090017578839044.jpg)
考虑上图中的蓝色圆,绿色的圆和蓝色的圆交于 A,B 2个交点 ,我们在逆时针系下考虑,那么 可以知道 对于蓝色的圆,它对应的某个 角度区间被覆盖了
假设 区间为 [A, B], 并且角度是按照 圆心到交点的 向量的 极角来定义 (为了方便,我一般都把角度转化到 [0,2pi]区间内) 那么可以知道在这种 标识情况下,可能存在以下情况
![](http://images0.cnblogs.com/blog2015/535397/201504/090018233525898.jpg)
这种区间的跨度如何解决呢?实际上十分简单,只需要把[A,B] 拆成 [A, 2PI], [0,B]即可
![](http://images0.cnblogs.com/blog2015/535397/201504/090018451023653.jpg)
下面介绍一下 对于我们当前所求任务的实际运用( 利用上述做法)
首先 对于所给的N个圆,我们可以进行去冗杂,表现为:
(1) 去掉包含(内含 or 内切)了某些圆的大圆
(2) 去掉相同的圆
(3) 如果存在2个圆不交,则直接返回 0 (交集必然为空)
经过以上几步,可以发现得到的圆都是两两相交的。于是枚举一个圆,并对于剩下的圆和它求交点,对于所求的的交点,可以得到一个角度区间 [A,B], 当然区间如果跨越了(例如 [1.5PI, 0.5PI],注意这里是有方向的) 2PI那么需要拆 区间
可以知道,最后区间的交集必然是最后 所有圆交集的一个边界!
于是我们在得到区间的情况下累积弓形的面积
提示: 在获得区间的过程中可能存在下面的情况,请注意!
![](http://images0.cnblogs.com/blog2015/535397/201504/090019115248347.jpg)
![](http://images0.cnblogs.com/blog2015/535397/201504/090019301336662.jpg)
显然包括上面的2个情况,注意使用“归一化”的判断思想来获得区间! (左图是 [A,B],右图是 [B,A],注意顺序!)
之后把当前的"点" 假如S集合中
最后的答案累加上S的凸包的面积即可(XXXXX)
完全不需要再求一次凸包,获得区间以后就获得了唯一的S凸包中的一条边,那么我们只需要累加他们叉乘的和! (可以理解为一边保存边一边算面积!,代码长度缩短 10%以上)
枚举圆 O(n)
每一次求交O(n)
获得的交点区间排序,离散化,求交 O(nlogn) (至多 O(2* n)个区间)
最后的求凸包 O(nlogn) (交点不会太多,至多2*n)
所以总的复杂度为 O(n^2 log n)
【题目推荐】
http://acm.hdu.edu.cn/showproblem.php?pid=3467
赤裸裸的圆交题目, 显然标程 被CHA ,此题数据比较水 (我至少用4个版本的错误代码AC)
http://acm.hdu.edu.cn/showproblem.php?pid=3239
虽然没有上面那题那么赤裸裸,可是推出模型以后直接用容斥 + 求圆交……
PS.这种做法如果实现的好,那么精度是比较高的!