好久没做zoj了,一做就是WA一版啊啊啊啊啊!!!
这个题,给你N个圆,求相交后把平面分成了多少份。
我开始找规律的,找不太对,后来一搜,发现是欧拉定理,真无语了。刚学完离散,没想到真能用得上。
不过这个找边数和面数很纠结啊。
根据欧拉定理,连通平面图(圆相交的图一定是平面图)满足 V - E + R = 2。 其中V是顶点数,E是边数,R是面数。这题就是求面数,但是这个不一定是连通的平面图。
所以需要转换下。可以证得,如果一个图中有X个连通的平面图,那么满足,V - E + R = X+1。(某张离散卷子考试题T T ,只需要将每个连通分支按题意累和,再考虑最后重叠的最外面即可)
所以就转换成求这个图的边数和顶点数。
没想到好方法,最水的方法,就是两两相交求交点(包括切点),因为可能这些点会有重复的,比如三个圆的交点重合了三个等等,所以需要排除重合的点,我直接用快排然后筛掉想同的点。然后判断这些点(每个点都不同)在多少个圆上。画图可知,一个圆上有N个点,那么这个圆被分割成N条边,所以只要判断这些点在多少个圆上即可。
P.S.:因为这题,学了好几个圆的函数,收获还是不小滴,就是这代码有点长了。。。
#include <queue>
#include <stack>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <limits.h>
#include <string.h>
#include <string>
#include <algorithm>
using namespace std;
const int MAX = 51;
struct point{ double x,y;};
struct circle{ point a;double r;};
circle c[MAX];
point p[MAX*MAX];
const double eps = 1e-8;
bool dy(double x,double y) { return x > y + eps;} // x > y
bool xy(double x,double y) { return x < y - eps;} // x < y
bool dyd(double x,double y) { return x > y - eps;} // x >= y
bool xyd(double x,double y) { return x < y + eps;} // x <= y
bool dd(double x,double y) { return fabs( x - y ) < eps;} // x == y
double disp2p(point a,point b)
{
return sqrt( ( a.x - b.x ) * ( a.x - b.x ) + ( a.y - b.y ) * ( a.y - b.y ) );
}
bool c2c_inst(circle a,circle b)
{
if( xy(disp2p(a.a,b.a),a.r+b.r) && dy(disp2p(a.a,b.a),fabs(a.r - b.r)) )
return true;
return false;
}
point c2c_tangent_p(circle a,circle b)
{
point t;
if( dd(disp2p(a.a,b.a),a.r+b.r) )
{
t.x = (a.r*b.a.x + b.r*a.a.x)/(a.r + b.r);
t.y = (a.r*b.a.y + b.r*a.a.y)/(a.r + b.r);
return t;
}
t.x = (a.r*b.a.x - b.r*a.a.x)/(a.r - b.r);
t.y = (a.r*b.a.y - b.r*a.a.y)/(a.r - b.r);
return t;
}
bool c2c_tangent(circle a,circle b)
{
if( dd(disp2p(a.a,b.a),a.r+b.r) || dd(disp2p(a.a,b.a),fabs(a.r-b.r)) )
return true;
return false;
}
bool cmp(point a,point b)
{
if( dd(a.x,b.x) ) return xy(a.y,b.y);
return xy(a.x,b.x);
}
point l2l_inst_p(point u1,point u2,point v1,point v2)
{
point ans = u1;
double t = ((u1.x - v1.x)*(v1.y - v2.y) - (u1.y - v1.y)*(v1.x - v2.x))/
((u1.x - u2.x)*(v1.y - v2.y) - (u1.y - u2.y)*(v1.x - v2.x));
ans.x += (u2.x - u1.x)*t;
ans.y += (u2.y - u1.y)*t;
return ans;
}
void l2c_inst_p(point c,double r,point l1,point l2,point &p1,point &p2)
{
point p = c;
double t;
p.x += l1.y - l2.y;
p.y += l2.x - l1.x;
p = l2l_inst_p(p,c,l1,l2);
t = sqrt(r*r - disp2p(p,c)*disp2p(p,c))/disp2p(l1,l2);
p1.x = p.x + (l2.x - l1.x)*t;
p1.y = p.y + (l2.y - l1.y)*t;
p2.x = p.x - (l2.x - l1.x)*t;
p2.y = p.y - (l2.y - l1.y)*t;
}
void c2c_inst_p(point c1,double r1,point c2,double r2,point &p1,point &p2)
{
point u,v;
double t;
t = (1 + (r1*r1 - r2*r2)/disp2p(c1,c2)/disp2p(c1,c2))/2;
u.x = c1.x + (c2.x - c1.x)*t;
u.y = c1.y + (c2.y - c1.y)*t;
v.x = u.x + c1.y - c2.y;
v.y = u.y - c1.x + c2.x;
l2c_inst_p(c1,r1,u,v,p1,p2);
}
int ee;
void cntee(point v,int n)
{
for(int i=0; i<n; i++)
if( dd((v.x - c[i].a.x)*(v.x - c[i].a.x) + (v.y - c[i].a.y)*(v.y - c[i].a.y),c[i].r*c[i].r) )
ee++;
}
bool used[MAX];
void DFS(int x,int n)
{
for(int i=0; i<n; i++)
if( !used[i] && ( c2c_inst(c[i],c[x]) || c2c_tangent(c[i],c[x]) ) )
{
used[i] = true;
DFS(i,n);
}
}
int main()
{
int n,ncases;
scanf("%d",&ncases);
while( ncases-- )
{
scanf("%d",&n);
for(int i=0; i<n; i++)
scanf("%lf%lf%lf",&c[i].a.x,&c[i].a.y,&c[i].r);
int ans,cnt = 0,sum = 0;
ee = 0;
memset(used,false,sizeof(used));
for(int i=0; i<n; i++)
if( !used[i] )
{
used[i] = true;
sum++;
DFS(i,n);
}
for(int i=0; i<n; i++)
for(int k=i+1; k<n; k++)
{
if( c2c_tangent(c[i],c[k]) )
p[cnt++] = c2c_tangent_p(c[i],c[k]);
if( c2c_inst(c[i],c[k]) )
c2c_inst_p(c[i].a,c[i].r,c[k].a,c[k].r,p[cnt++],p[cnt++]);
}
sort(p,p+cnt,cmp);
int nn = 0;
if( cnt )
{
cntee(p[0],n);
nn = 1;
}
for(int i=1; i<cnt; i++)
if( !dd(p[i].x,p[i-1].x) || !dd(p[i].y,p[i-1].y) )
{
nn++;
cntee(p[i],n);
}
ans = ee - nn + sum + 1;
printf("%d\n",ans);
}
return 0;
}