LA2572 UVA1308

Do you know confetti? They are small discs of colored paper, and people throw them around during parties or festivals. Since people throw lots of confetti, they may end up stacked one on another, so there may be hidden ones underneath.

A handful of various sized confetti have been dropped on a table. Given their positions and sizes, can you tell us how many of them you can see?

The following figure represents the disc configuration for the first sample input, where the bottom disc is still visible.

\epsfbox{p2572.eps}

Input 

The input is composed of a number of configurations of the following form.


n   
x1 y1 r1
x2 y2 r2
$ \vdots$   
xn yn rn


The first line in a configuration is the number of discs in the configuration (a positive integer not more than 100), followed by one Ine descriptions of each disc: coordinates of its center and radius, expressed as real numbers in decimal notation, with up to 12 digits after the decimal point. The imprecision margin is $ \pm$5 x 10-13 . That is, it is guaranteed that variations of less than $ \pm$5 x 10-13 on input values do not change which discs are visible. Coordinates of all points contained in discs are between -10 and 10.

Confetti are listed in their stacking order, x1 y1 r1 being the bottom one and xn yn rn the top one. You are observing from the top.

The end of the input is marked by a zero on a single line.

Output 

For each configuration you should output the number of visible confetti on a single line.

Sample Input 

3
0 0 0.5
-0.9 0 1.00000000001
0.9 0 1.00000000001
5
0 1 0.5
1 1 1.00000000001
0 2 1.00000000001
-1 1 1.00000000001
0 -0.00001 1.00000000001
5
0 1 0.5
1 1 1.00000000001
0 2 1.00000000001
-1 1 1.00000000001
0 0 1.00000000001
2
0 0 1.0000001
0 0 1
2
0 0 1
0.00000001 0 1
0

Sample Output 

3
5
4
2

2

// LA2572/UVa1308 Viva Confetti
// Rujia Liu
#include<cmath>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;

const double eps = 5 * 1e-13;
int dcmp(double x) {
  if(fabs(x) < eps) return 0; else return x < 0 ? -1 : 1;
}

const double PI = acos(-1);
const double TWO_PI = PI * 2;

double NormalizeAngle(double rad, double center = PI) {
  return rad - TWO_PI * floor((rad + PI - center) / TWO_PI);
}

struct Point {
  double x, y;
  Point(double x=0, double y=0):x(x),y(y) { }
};

typedef Point Vector;

Vector operator + (Vector A, Vector B) { return Vector(A.x+B.x, A.y+B.y); }
Vector operator - (Point A, Point B) { return Vector(A.x-B.x, A.y-B.y); }
Vector operator * (Vector A, double p) { return Vector(A.x*p, A.y*p); }
Vector operator / (Vector A, double p) { return Vector(A.x/p, A.y/p); }

double Dot(Vector A, Vector B) { return A.x*B.x + A.y*B.y; }
double Length(Vector A) { return sqrt(Dot(A, A)); }

double angle(Vector v) {
  return atan2(v.y, v.x);
}

// 交点相对于圆1的极角保存在rad中
void getCircleCircleIntersection(Point c1, double r1, Point c2, double r2, vector<double>& rad) {
  double d = Length(c1 - c2);
  if(dcmp(d) == 0) return; // 不管是内含还是重合,都不相交
  if(dcmp(r1 + r2 - d) < 0) return;
  if(dcmp(fabs(r1-r2) - d) > 0) return;

  double a = angle(c2 - c1);
  double da = acos((r1*r1 + d*d - r2*r2) / (2*r1*d));
  rad.push_back(NormalizeAngle(a-da));
  rad.push_back(NormalizeAngle(a+da));
}

const int maxn = 100 + 5;
int n;
Point center[maxn];
double radius[maxn];
bool vis[maxn];

// 覆盖点p的最上层的圆
int topmost(Point p) {
  for(int i = n-1; i >= 0; i--)//自顶向底 枚举每个圆看能不能覆盖这个点!
    if(Length(center[i]-p) < radius[i]) return i;
  return -1;
}

int main() {
  while(cin >> n) {
    if(!n) break;
    for(int i = 0; i < n; i++) {
      double x, y, r;
      cin >> x >> y >> r;
      center[i] = Point(x, y);
      radius[i] = r;
    }
    memset(vis, 0, sizeof(vis));
    for(int i = 0; i < n; i++) { // 自底向上 枚举每个圆。
      // 考虑圆i被切割成的各个圆弧。把圆周当做区间来处理,起点是0,终点是2PI
      vector<double> rad;
      rad.push_back(0);
      rad.push_back(PI*2);/**对于那些和其他圆没有交点的圆,那么这段小圆弧就是整个圆周**/
      for(int j = 0; j < n; j++)
        getCircleCircleIntersection(center[i], radius[i], center[j], radius[j], rad);//求出每个圆与 该圆i 的交点。
      sort(rad.begin(), rad.end());//按极角排序

      for(int j = 0; j < rad.size(); j++) { //枚举每个小圆弧
        double mid = (rad[j] + rad[j+1]) / 2.0; // 圆弧中点相对于圆i圆心的极角
        for(int side = -1; side <= 1; side += 2) {
          double r2 = radius[i] - side*eps; // 往里面或者外面稍微一动一点点
          int t = topmost(Point(center[i].x + cos(mid)*r2, center[i].y + sin(mid)*r2));//看这个稍稍移动的点 在哪个圆上。
          if(t >= 0) vis[t] = true;//标记这个点在的小圆弧为真。
        }
      }
    }
    int ans = 0;
    for(int i = 0; i < n; i++) if(vis[i]) ans++;
    cout << ans << "\n";
  }
  return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值