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.
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); }
若点在直线上(线段上或者延长线上)返回值为零。
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; }