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
题意
如果线段A和线段B相交,线段B和线段C相交,那么线段A、B、C属于同一个线段集,现在给n次操作,当操作为P的时候添加一个线段,当操作为Q时查询第k个线段所在的线段集里面有多少个线段。
题解
本题是一个并查集的问题。
如果不了解什么是并查集,可以到下面这个链接里面去看,我觉得讲得很好,第一次了解并查集就是看这个看懂的。
http://blog.youkuaiyun.com/qzc295919009/article/details/23306781
如果你已经了解什么是并查集,那么本题的关键问题就是如何判断两条线段在同一个集合,也就是判断线段相交了。
线段相交在计算几何中用快速排斥实验和跨立实验来进行验证,一般应该不了解吧,可以自己上网搜,我就不贴链接了。
现在假设你已经了解了快速排斥实验和跨立实验,那我来讲讲代码实现。
假设有A1、A2、B1、B2四个点
1、快速排斥实验:
快速排斥实验是看以A1A2、B1B1为对角线的矩阵是否相交,如果不相交的话线段不可能相交,如果相交的话线段可能相交。
那么如何判断这两个矩阵是否相交呢,判断这个四个点的x、y区间是否相交就好了,可以到纸上面画一下,代码如下:
bool check()
{
if(min(A1.x,A2.x) <= max(B1.x,B2.x) &&
min(B1.x,B2.x) <= max(A1.x,A2.x) &&
min(A1.y,A2.y) <= max(B1.y,B2.y) &&
min(B1.y,B2.y) <= max(A1.y,A2.y))
{
if(kua())//kua()是跨立实验的函数
return true;
}
return false;
}
2、跨立实验
如果线段A1A2和线段B1B2相交,那么A1、A2会分别在B1B2两边,则(A1-B1)X(B2-B1)和(A2-B1)X(B2-B1)异号,即:
(A1-B1)X(B2-B1)*(A2-B1)X(B2-B1)<0
如果在同一边,那么就会大于0,如果两个点都在边上,那么两条线段在同一条直线上,因为经过快速排斥实验所以也是相交的,所以我们可以用下面的表达式判断:
(A1-B1)X(B2-B1)*(B2-B1)X(A2-B1)>=0
同理,对于B1、B2两个点也一样
代码如下:
double xx(Point &s, Point &t)
{
return (s.x*t.y-s.y*t.x);
}
double kua()
{
A1B1.x = A1.x-B1.x; A1B1.y = A1.y - B1.y;
B2B1.x = B2.x-B1.x; B2B1.y = B2.y - B1.y;
A2A1.x = A2.x-A1.x; A2A1.y = A2.y - A1.y;
B2A1.x = B2.x-A1.x; B2A1.y = B2.y - A1.y;
A2B1.x = A2.x-B1.x; A2B1.y = A2.y - B1.y;
if(xx(A1B1,B2B1)*xx(B2B1,A2B1)>=0)
{
A1B1.y = -A1B1.y; A1B1.x = -A1B1.x;
if(xx(A1B1,A2A1)*xx(A2A1,B2A1)>=0)
return 1;
return 0;
}
return 0;
}
这样我们就可以判断两个线段是否相交,完整AC代码如下。
#include <iostream>
#include <stdio.h>
#include <cmath>
#define MAXN 1010
using namespace std;
struct Point
{
double x,y;
};
struct Edge
{
Point a,b;
};
Edge e[MAXN];
int fa[MAXN];
int sz[MAXN];
Point A1,A2,B1,B2;
Point A1B1, A2B1, B2B1, A2A1, B2A1;
double xx(Point &s, Point &t);
double kua();
bool check();
void init(int n);
int find(int x);
void merge(int u, int v);
int main()
{
//freopen("in.txt", "r", stdin);
int T,n;
char mode[10];
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
init(n);
int k = 0;
for(int i = 0; i < n; i++)
{
scanf("%s", &mode);
if(mode[0] == 'P')
{
k++;
scanf("%lf%lf%lf%lf",&e[k].a.x,&e[k].a.y,&e[k].b.x,&e[k].b.y);
B1 = e[k].a; B2 = e[k].b;
for(int j = 1; j < k; j++)
{
A1 = e[j].a; A2 = e[j].b;
if(find(j) != find(k) && check())
{
//cout << j << " " << k << endl;
merge(j,k);
}
}
}
else
{
int tmp;
scanf("%d", &tmp);
printf("%d\n", sz[find(tmp)]);
}
}
if(T != 0)
printf("\n");
}
return 0;
}
double xx(Point &s, Point &t)
{
return (s.x*t.y-s.y*t.x);
}
double kua()
{
A1B1.x = A1.x-B1.x; A1B1.y = A1.y - B1.y;
B2B1.x = B2.x-B1.x; B2B1.y = B2.y - B1.y;
A2A1.x = A2.x-A1.x; A2A1.y = A2.y - A1.y;
B2A1.x = B2.x-A1.x; B2A1.y = B2.y - A1.y;
A2B1.x = A2.x-B1.x; A2B1.y = A2.y - B1.y;
if(xx(A1B1,B2B1)*xx(B2B1,A2B1)>=0)
{
A1B1.y = -A1B1.y; A1B1.x = -A1B1.x;
if(xx(A1B1,A2A1)*xx(A2A1,B2A1)>=0)
return 1;
return 0;
}
return 0;
}
bool check()
{
if(min(A1.x,A2.x) <= max(B1.x,B2.x) &&
min(B1.x,B2.x) <= max(A1.x,A2.x) &&
min(A1.y,A2.y) <= max(B1.y,B2.y) &&
min(B1.y,B2.y) <= max(A1.y,A2.y))
{
if(kua())
return true;
}
return false;
}
void init(int n)
{
for(int i = 1; i <= n; i++)
{
sz[i] = 1;
fa[i] = i;
}
}
int find(int x)
{
return fa[x] == x?x:fa[x]=find(fa[x]);
}
void merge(int u, int v)
{
int fu = find(u), fv = find(v);
if(fu != fv)
{
fa[fv] = fu;
sz[fu] += sz[fv];
sz[fv] = 0;
}
}