Segment set
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3486 Accepted Submission(s): 1297
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.
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
线段相交分为两种情况:一、线段相互交叉,即相互跨立 。二、一条线段的某个端点在另一条线段上。
double cross(point a,point b,point c) //叉积判断点与直线位置关系
{ //若点在直线逆时针方向,返回正值
return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y); //<span style="font-family: Arial, Helvetica, sans-serif;">若点在直线顺时针方向,返回负值</span>
} //若点在线段上(或延长线)上返回0
bool ifin(point a,point b,point c) //判断点是否落在线段上
{
if(c.x>=min(a.x,b.x)&&c.y>=min(a.y,b.y)&&
c.x<=max(a.x,b.x)&&c.y<=max(a.y,b.y))
return true;
return false;
}
则判断线段是否相交,可用下面算法:
bool judge(point a,point b,point c,point d)
{
//快速排斥实验,不想交返回0
if(cross(a,b,c)==0&&ifin(a,b,c))
return true;
else if(cross(a,b,d)==0&&ifin(a,b,d))
return true;
else if(cross(c,d,a)==0&&ifin(c,d,a))
return true;
else if(cross(c,d,b)==0&&ifin(c,d,b))
return true;
//跨立实验,线段ab的两端点在cd两端,且线段cd的两端点在线段ab两端
if(cross(a,b,c)*cross(a,b,d)<0&&cross(c,d,a)*cross(c,d,b)<0)
return true;
return false;
}
思路:每次得到一条线段判断和前面所有线段是否相交,运用并查集相关操作,得到答案。
#include"stdio.h"
#include"string.h"
#include"math.h"
#include"vector"
#include"iostream"
#include"algorithm"
using namespace std;
#define N 1005
const int inf=0x7fffffff;
int pre[N],num[N];
struct point
{
double x,y;
};
struct node
{
point p1,p2;
}f[N];
double cross(point a,point b,point c) //叉积判断点与直线位置关系
{ //若点在直线逆时针方向,返回正值
return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
}
bool ifin(point a,point b,point c)
{
if(c.x>=min(a.x,b.x)&&c.y>=min(a.y,b.y)&&
c.x<=max(a.x,b.x)&&c.y<=max(a.y,b.y))
return true;
return false;
}
bool judge(point a,point b,point c,point d)
{
//快速排斥实验,不想交返回0
if(cross(a,b,c)==0&&ifin(a,b,c))
return true;
else if(cross(a,b,d)==0&&ifin(a,b,d))
return true;
else if(cross(c,d,a)==0&&ifin(c,d,a))
return true;
else if(cross(c,d,b)==0&&ifin(c,d,b))
return true;
//跨立实验,线段ab的两端点在cd两端,且线段cd的两端点在线段ab两端
if(cross(a,b,c)*cross(a,b,d)<0&&cross(c,d,a)*cross(c,d,b)<0)
return true;
return false;
}
int findx(int x)
{
if(x!=pre[x])
pre[x]=findx(pre[x]);
return pre[x];
}
void unionset(int a,int b)
{
int f1,f2;
f1=findx(a);
f2=findx(b);
if(f1!=f2)
{
pre[f1]=f2;
num[f2]+=num[f1];
}
}
int main()
{
int T,n,i,j,k,x;
char ch;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
{
pre[i]=i;
num[i]=1;
}
for(i=1,j=1;i<=n;i++)
{
getchar();
scanf("%c",&ch);
if(ch=='Q')
{
scanf("%d",&x);
int xx=findx(x);
printf("%d\n",num[xx]);
}
else
{
scanf("%lf%lf%lf%lf",&f[j].p1.x,&f[j].p1.y,&f[j].p2.x,&f[j].p2.y);
for(k=1;k<j;k++)
{
if(judge(f[k].p1,f[k].p2,f[j].p1,f[j].p2))
unionset(k,j);
}
j++;
}
}
if(T)
puts("");
}
return 0;
}