(hdu step 5.1.3)Segment set(求与一条线段相交的线段集合中的线段的数量)

题目:

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).
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


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;
}
时间: 2024-10-11 11:40:58

(hdu step 5.1.3)Segment set(求与一条线段相交的线段集合中的线段的数量)的相关文章

(hdu step 3.1.5)Tiling_easy version(求用2*1、2*2两种骨格铺满2*n的网格的方案数)

在写题解之前给自己打一下广告哈~..抱歉了,希望大家多多支持我在CSDN的视频课程,地址如下: http://edu.csdn.net/course/detail/209 题目: Tiling_easy version Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 515 Accepted Submission(s): 447   Prob

(hdu step 7.1.5)Maple trees(求凸包的最小覆盖圆的半径)

题目: Maple trees Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 177 Accepted Submission(s): 63   Problem Description There are a lot of trees in HDU. Kiki want to surround all the trees with the m

(hdu step 4.2.3)Knight Moves(求从起点是否能够到达终点的最小步数)

题目: Knight Moves Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 453 Accepted Submission(s): 326   Problem Description A friend of you is doing research on the Traveling Knight Problem (TKP) where

(hdu step 5.1.5)Dragon Balls(求并查集的根节点、节点数和个结点的移动次数)

题目: Dragon Balls Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 562 Accepted Submission(s): 239   Problem Description Five hundred years later, the number of dragon balls will increase unexpected

(hdu step 1.3.6)Wooden Sticks(求长度和重量都严格递增的数列的个数)

题目: Wooden Sticks Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 2374 Accepted Submission(s): 921   Problem Description There is a pile of n wooden sticks. The length and weight of each stick are

(hdu step 3.1.6)统计问题(求不断地左右走、向上走n步的方案数)

在写题解之前给自己打一下广告哈~..抱歉了,希望大家多多支持我在CSDN的视频课程,地址如下: http://edu.csdn.net/course/detail/209 题目: 统计问题 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 732 Accepted Submission(s): 466   Problem Description

(hdu step 2.3.3)Big Number(求N!的位数)

在写题解之前给自己打一下广告哈~..抱歉了,希望大家多多支持我在CSDN的视频课程,地址如下: http://edu.csdn.net/course/detail/209 题目: Big Number Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 951 Accepted Submission(s): 640   Problem Des

(hdu step 6.3.1)Strategic Game(求用最少顶点数把所有边都覆盖,使用的是邻接表)

题目: Strategic Game Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 110 Accepted Submission(s): 75   Problem Description Bob enjoys playing computer games, especially strategic games, but sometim

(hdu step 6.1.3)畅通工程(求需要添加多少条边才能让n个点连通)

题目: 畅通工程 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 163 Accepted Submission(s): 132   Problem Description 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇.省政府"畅通工程"的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道