Segment set
Time Limit: 3000/1000 MS (Java/Others)
Memory Limit: 32768/32768 K (Java/Others)
Problem Description
A segment and all segments which are connected with it compose a segment set. The size of a segment set is the number of segments in it. The problem is to find the size of some segment set.
Input
In the first line there is an integer t - the number of test case. For each test case in first line there is an integer n (n<=1000) - the number of commands.
There are two different commands described in different format shown below:
P x1 y1 x2 y2 - paint a segment whose coordinates of the two endpoints are (x1,y1),(x2,y2).
Q k - query the size of the segment set which contains the k-th segment.
k is between 1 and the number of segments in the moment. There is no segment in the plane at first, so the first command is always a P-command.
Output
For each Q-command, output the answer. There is a blank line between test cases.
Sample Input
1
10
P 1.00 1.00 4.00 2.00
P 1.00 -2.00 8.00 4.00
Q 1
P 2.00 3.00 3.00 1.00
Q 1
Q 3
P 1.00 4.00 8.00 2.00
Q 2
P 3.00 3.00 6.00 -2.00
Q 5
Sample Output
1
2
2
2
5
思路:
题意:在一个平面直角坐标系中,通过P操作加入线段,如果两个线段相交,则认为他们是一个集合里面的。Q操作询问当前情况下第k条线段所在的集合里面有几条线段(与线段k相交或间接相交<1相交2,2相交3,则1间接相交3>的线段有几条)。输出后测试用例间要有空行。
解决:计算几何求线段相交+并查集求集合内个数。
求线段相交方法:快速排斥+跨立。套用模板。
AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 1000001
#define man 1e-12
int tree[maxn];//并查集
int num[maxn];//集合元素数
//点
struct point{
double x;
double y;
};
//线段
struct line{
point a;
point b;
}lline[maxn];
//判断线段是否相交
bool judge(line l1,line l2)
{
point p1=l1.a;//线段l1的两个端点p1,p2
point p2=l1.b;//p1(p1.x,p1.y) p2(p2.x,p2.y)
point p3=l2.a;//线段l2的两个端点p3,p4
point p4=l2.b;//p3(p3.x,p3.y) p4(p4.x,p4.y)
if(min(p1.x,p2.x)>max(p3.x,p4.x) ||
min(p1.y,p2.y)>max(p3.y,p4.y) ||
min(p3.x,p4.x)>max(p1.x,p2.x) ||
min(p3.y,p4.y)>max(p1.y,p2.y))
{
return 0;
}
double k1,k2,k3,k4;
k1=(p2.x-p1.x)*(p3.y-p1.y)-(p2.y-p1.y)*(p3.x-p1.x);
k2=(p2.x-p1.x)*(p4.y-p1.y)-(p2.y-p1.y)*(p4.x-p1.x);
k3=(p4.x-p3.x)*(p1.y-p3.y)-(p4.y-p3.y)*(p1.x-p3.x);
k4=(p4.x-p3.x)*(p2.y-p3.y)-(p4.y-p3.y)*(p2.x-p3.x);
return (k1*k2<=man && k3*k4<=man);
}
int findroot(int x)//查找函数
{
while(tree[x]!=x) x=tree[x];
return x;
}
void join(int x,int y)//合并函数
{
int fx=findroot(x);
int fy=findroot(y);
if(fx!=fy)
{
tree[fx]=fy;
num[fy]+=num[fx];
}
}
int main()
{
int t,n;
char ch[2];
cin>>t;
while(t--)
{
cin>>n;
//init
for(int i=1;i<maxn;i++)
{
tree[i]=i;
num[i]=1;
}
int i=0;//输入的是第几条线段
while(n--)
{
cin>>ch;
if(ch[0]=='P')
{
i++;
//输入线段信息
cin>>lline[i].a.x>>lline[i].a.y;
cin>>lline[i].b.x>>lline[i].b.y;
//判断目前的线段是否相交
for(int j=1;j<i;j++)
{
if(judge(lline[i],lline[j]))
{
join(i,j);
}
}
}
else
{
int k;
cin>>k;//要查找第k条线段的连通集
cout<<num[findroot(k)]<<endl;
}
}
if(t!=0) cout<<endl;
}
return 0;
}