杭电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;
}