题意: 输入每一条边,如果两个边相交则同为一个集合,问题是求某一条边所在集合的边的数目。
很明显的并查集,但是怎么判断边与边的相交呢?这里用到了模板。
知道了边相交,那么find 和 merge 便不难了。
注意:
1. 用num存储集合边的数目
2. 类的简单使用,edge存储边的起始点
3. Find里面用到了优化
#include<cstdio>
#include<cmath>
#include<iostream>
#define Max 1010
using namespace std;
class point
{
public:
double x,y;
};
class edge
{
public:
point a,b;
}e[Max];
int root[Max];
int num[Max];
//-----------------------------------------------------------------------------------
double xmult(point a,point b,point c)
{
return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
bool OnSegment(point a,point b,point c)
{
return c.x>=min(a.x,b.x)&&c.x<=max(a.x,b.x)&&c.y>=min(a.y,b.y)&&c.y<=max(a.y,b.y);
}
bool Cross(point a,point b,point c,point d) //判断是否相交
{
double d1,d2,d3,d4;
d1=xmult(c,d,a);
d2=xmult(c,d,b);
d3=xmult(a,b,c);
d4=xmult(a,b,d);
if(d1*d2<0&&d3*d4<0) return 1;
else if(d1==0&&OnSegment(c,d,a)) return 1;
else if(d2==0&&OnSegment(c,d,b)) return 1;
else if(d3==0&&OnSegment(a,b,c)) return 1;
else if(d4==0&&OnSegment(a,b,d)) return 1;
return 0;
}
//----------------------------------------------------------------------------------
int Find(int x)
{
int _x = x;
while(_x != root[_x])
_x = root[_x];
while(x != root[x]){
int i = root[x];
root[x] = _x;
x = i;
}
return _x;
}
void Merge(int u,int v)
{
int _u = Find(u);
int _v = Find(v);
if(_u != _v){
root[_u] = _v;
num[_v] += num[_u];
}
}
int main()
{
// freopen("in.txt","r",stdin);
int ncase;
scanf("%d",&ncase);
while(ncase--){
int n;
scanf("%d",&n);
for(int i = 1;i <= n; i++){
root[i] = i;
num[i] = true;
}
int edgenum = 0;
getchar();
for(int i = 1;i <= n; i++){
char c;
scanf("%c",&c);
if(c == 'P'){
edgenum++;
scanf("%lf%lf%lf%lf",&e[edgenum].a.x,&e[edgenum].a.y,&e[edgenum].b.x,&e[edgenum].b.y);
for(int j = 1;j < edgenum; j++){
if(Find(j) != Find(edgenum)) {
if(Cross(e[j].a,e[j].b,e[edgenum].a,e[edgenum].b))
Merge(j,edgenum);
}
}
}
else if(c == 'Q') {
int ans;
scanf("%d",&ans);
printf("%d\n",num[Find(ans)]);
}
getchar();
}
if(ncase)
printf("\n");
}
return 0;
}