杭电1558——带权并差集+线段判交

杭电1558——带权并差集+线段判交

原题传送门带权并差集简单讲解线段判交向量叉乘
写在题前:
带权并差集的简单应用,不过融合了线段判交的一个题。
刚开始不知道线段判交的知识,所以感觉没什么思路。恶补了之后,就很容易ac了。

解题思路:

  • 用输入的线段,依次和之前所有的线段判断是否相交。相交的,用meg函数建立关系。
  • 查询的时候找到最上面的节点的权值就是答案。

写在题后:

  • 这道题基本没坑!注意每个样例之间用空行隔开,最后一个样例后面没有空行。

ac代码:

# include <iostream>
# include <algorithm>
# include <cstdio>
# include <cstdlib>
# include <string>
# include <cstring>
# include <vector>
# include <map>
using namespace std;

struct p {
	double x;
	double y;
};//记录一个点
struct line {
	p a;
	p b;
};//记录一个线段

const int maxn = 1010;
int id[maxn];
int num[maxn];//记录权值

int fin(int x) {
//使用路径压缩,因为meg函数已经更新过权值,所以这里不用更新
	return id[x] = x == id[x] ? x : fin(id[x]);
}

void meg(int x, int y) {
	int a = fin(x);
	int b = fin(y);
	if (a != b) {//x和y建立关系,同时更新权值。
		id[b] = a;
		num[a] += num[b];
	}
}

bool same(line k, line t) {
	double minkx = min(k.a.x, k.b.x);
	double minky = min(k.a.y, k.b.y);
	double maxkx = max(k.a.x, k.b.x);
	double maxky = max(k.a.y, k.b.y);

	double mintx = min(t.a.x, t.b.x);
	double minty = min(t.a.y, t.b.y);
	double maxtx = max(t.a.x, t.b.x);
	double maxty = max(t.a.y, t.b.y);

	double minfx = max(minkx, mintx);
	double minfy = max(minky, minty);
	double maxfx = min(maxkx, maxtx);
	double maxfy = min(maxky, maxty);
	if (minfx > maxfx || minfy > maxfy) {
		return false;
	}
	//以上是判断两条线段是否相交的第一步:快速排斥

	double x0, y0, x1, y1, x2, y2;

	x0 = k.b.x - k.a.x;
	y0 = k.b.y - k.a.y;
	x1 = t.a.x - k.a.x;
	y1 = t.a.y - k.a.y;
	x2 = t.b.x - k.a.x;
	y2 = t.b.y - k.a.y;
	double u1 = x0 * y1 - y0 * x1;
	double v1 = x0 * y2 - y0 * x2;
	//以上是判断t线段的两个点是否在k线段的两边

	x0 = t.b.x - t.a.x;
	y0 = t.b.y - t.a.y;
	x1 = k.a.x - t.a.x;
	y1 = k.a.y - t.a.y;
	x2 = k.b.x - t.a.x;
	y2 = k.b.y - t.a.y;
	double u2 = x0 * y1 - y0 * x1;
	double v2 = x0 * y2 - y0 * x2;
	//以上是判断k线段的两个点是否在t线段的两边
	
	return (u1*v1 <= 1e-10 && u2*v2 <= 1e-10);
}

int main() {
	ios::sync_with_stdio(false);
	int T;
	cin >> T;
	while (T--) {
		int n;
		cin >> n;
		for (int i = 0; i < maxn; i++) {
			id[i] = i;
			num[i] = 1;
		}
		vector <line>l;
		line ll;
		ll.a.x = ll.a.y = ll.b.x = ll.b.y = 0.0;
		l.push_back(ll);//因为是从1开始计数,因此先放进一个线段。
		//注意初始化。

		string s;
		while (n--) {
			cin >> s;
			if (s[0] == 'P') {
				line tl;
				cin >> tl.a.x >> tl.a.y >> tl.b.x >> tl.b.y;
				for (int j = 1; j < l.size(); j++) {
					if (same(l[j], tl)) {
						meg(j, l.size());
					}
				}
				l.push_back(tl);
			}
			else if (s[0] == 'Q') {
				int t;
				cin >> t;
				cout << num[fin(t)] << endl;;
			}
		}
		if (T != 0) {//最后一行后面不用空行
			cout << endl;
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值