题目:
Segment set |
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) |
Total Submission(s): 177 Accepted Submission(s): 82 |
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). 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 |
Author LL |
Source HDU 2006-12 Programming Contest |
Recommend LL |
题目大意:
在此对“线段集合”的概念解释一下:有A,B,C三条线段。如果A与B相交,B与C相交(线段A与线段C可以不直接相交).那么这时候A,B,C依然属于同一个线段集合。在输入样例时,遇到P代表的是添加一条新边,后面跟的是一条边的
起点的横纵坐标、终点的横纵坐标
题目分析:
计算几何+并查集。其实在这里只是用到了计算集合中的"相交"的概念,判断两条线段是否相交。大体思路就是,添加线段的时候,如果两条线段相交,就将着两条线段合并到同一个线段集合中。然后在Q的时候查询某一条
线段所在的线段集合中的线段的数量。
代码如下:
/* * c.cpp * * Created on: 2015年2月28日 * Author: Administrator */ #include <iostream> #include <cstdio> #include <algorithm> #include <cmath> using namespace std; const int maxn = 1001; int father[maxn];//用于保存父子关系 int r[maxn];//用于保存一某一个结点为根节点的权重。(在这里可以理解成某一个线段集合中的点段的数量).例如r[a]表示的是以a为根节点的集合的权重 int counter;//当前的边的数量 /** * 寻找某一个结点所在集合的根节点 */ int find(int a) { if (a == father[a]) { return a; } return father[a] = find(father[a]); } /** * 合并a结点所在的集合与结点b所在的集合 */ void join(int a, int b) { int fa = find(a);//找到结点a所在集合的根节点 int fb = find(b);//找到结点b所在集合的根节点 if (fa != fb) {//如果a所在集合的根节点与b所在集合的根节点不相同 father[fa] = fb;//则合并这两个集合.在这里执行的操作室将a所在集合的根节点fa指向结点b所在集合的根节点fb r[fb] += r[fa];//将结点fa的权重加到结点fb上 /** * 将结点fa的权重置0.为什么要这样做呢? * 主要是为了避免权重的重复相加。这是由后面的addEdge()这个函数的执行操作所决定的。 * 每次有新的边时,addEdge()都会遍历边集合中的所有边,判断其是否与新的边相交, * 如果相交,则执行并操作join().这时候其中一条鞭的权值就会移到另一条边上. * 例如有序号分别为1,2,3的三条边。他们依次添加道边的集合上.它们的权重分别是1,1,1.如果这三条边都属于同一个集合, * 那么这个集合的权重应该是1+1+1=3. * * 在添加边2的时候,假如边1和边2相交,那么这时候边2的权重是2 * 如果不把结点1的权重置0,那么在边3月边2、边1都相交的情况下, * 那么经过合并操作以后,得出的并查集的权重是1+2+1=4了,这很明显是不正确的 */ r[fa] = 0; } } struct Point {//点 double x;//横坐标 double y;//纵坐标 }; struct Edge {//边 Point start;//起点 Point end;//终点 }edges[maxn];//边集合 double multiply(Point p1, Point p2, Point p0) { return ((p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y)); } //a(x1,y1),b(x2,y2) //x1*y2-x2*y1 /** * 确定两条线段是否相交 */ int intersect(Edge u, Edge v) { return ((max(u.start.x, u.end.x) >= min(v.start.x, v.end.x)) && //u中最右的点是否在v最左的点的右边 (max(v.start.x, v.end.x) >= min(u.start.x, u.end.x)) && //v中最右的点是否在u最左的点的右边 //判断这两条线段在水平层面上是否可能相交 (max(u.start.y, u.end.y) >= min(v.start.y, v.end.y)) && //u中最上的点是否在v最下的点的上边 (max(v.start.y, v.end.y) >= min(u.start.y, u.end.y)) && //v中最上的点是否在u最下的点的上边 //判断这两条线段在垂直层面上是否可能相交 (multiply(v.start, u.end, u.start) * multiply(u.end, v.end, u.start) >= 0) && //判断v.start,v.end是否分布在u.end两侧(或线上) (multiply(u.start, v.end, v.start) * multiply(v.end, u.end, v.start) >= 0)); //判断u.start,u.end是否分布在v.start两侧(或线上) } /** * 添加边的操作 */ void addEdge(){ int i; for(i = 1 ; i < counter ; ++i){//新添加的边与目前边集合中的所有边作比较 if(intersect(edges[i],edges[counter]) == true){//判断它们是否相交 join(i,counter);//如果相交则执行合并操作 } } } /** * 初始化 */ void init(){ int i; for(i = 1 ; i < maxn ; ++i){//遍历所有节点.索引从1开始,不要从0开始.否则会有一些问题.认真想想为什么 father[i] = i;//所有节点的父亲节点一开始默认都是它自己 r[i] = 1;//所有节点默认的权重都是1 } } int main() { int t; scanf("%d",&t); int cas = 0;//主要用于没测测试用例后面都打印一个空行 while(t--){ if(cas != 0){//需要注意的是同样的表述.有的题目中全部打空行可以AC,有的必须最后一个不能打空行 printf("\n"); } cas++; counter = 1;//边数从1开始 init(); int m; scanf("%d",&m); string str; int i; for(i = 1 ; i <= m ; ++i){//需要注意一下这种形式的输入样例 cin >> str; if(str[0] == ‘P‘){ scanf("%lf%lf%lf%lf",&edges[counter].start.x,&edges[counter].start.y,&edges[counter].end.x,&edges[counter].end.y); addEdge(); counter++; }else{ int index; scanf("%d",&index); printf("%d\n",r[find(index)]); } } // printf("\n"); } return 0; }