zoj-3521 Fairy Wars

考点

  • 扫描线

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e4 + 50;
int n, r, l, cx, cy, fa[maxn], val[maxn];
bool vis[maxn];

int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }

void join(int x, int y) {
int a = find(x), b = find(y);
if (a == b) return;
val[a] += val[b];
fa[b] = a;
}

struct node1 {
int x_, y_;
bool operator<(node1 &x) { return x_ < x.x_; }
} arr[maxn];

struct node2 {
int y_, id_;
node2(int y, int id) : y_(y), id_(id) {}
bool operator<(const node2 &x) const {
if (y_ != x.y_) return y_ < x.y_;
return id_ < x.id_;
}
};
set<node2> st;

int main() {
while (cin >> n >> r >> l) {
l /= 2;
st.clear(), memset(vis, 0, sizeof(vis));
for (int i = 1; i <= n; ++i) {
cin >> arr[i].x_ >> arr[i].y_;
fa[i] = i, val[i] = 1;
}
cin >> cx >> cy;
sort(arr + 1, arr + 1 + n);
int head = 1, tail = 1;
while (head <= tail && tail <= n) {
while (head <= tail && arr[tail].x_ - arr[head].x_ > l) {
st.erase(node2(arr[head].y_, head));
++head;
}
auto it = st.emplace(arr[tail].y_, tail).first;
if (it != st.begin() && it->y_ - prev(it)->y_ <= l) {
join(prev(it)->id_, it->id_);
}
if (next(it) != st.end() && next(it)->y_ - it->y_ <= l) {
join(it->id_, next(it)->id_);
}
++tail;
}
ll x, y, f, ans = 0;
for (int i = 1; i <= n; ++i) {
x = abs(arr[i].x_ - cx), y = abs(arr[i].y_ - cy);
if (x * x + y * y <= 1ll * r * r) {
f = find(i);
if (!vis[f]) vis[f] = 1, ans += val[f];
}
}
cout << ans << endl;
}
return 0;
}

思路

题意是说,若两个点的x坐标相差l/2以内,且y坐标也相差l/2以内,那么这两个点视作同一集合;

若集合内某一点在圆内,则集合内的所有点都视作”冰块“。问你一共有多少冰块。

思路很简单:

  • 将点集对x轴排序,利用双指针筛选任意两点的x坐标差都在l/2以内的集合

  • 上述集合再对y轴排序,每个点比较自己前后位置点的y坐标差

    若两点y坐标差也满足题意,则加入到同一个并查集

    因为上述集合要满足动态按序增加和删除结构体,选择set实现

  • 最后遍历所有点,如果该点在圆内,则其所在的并查集的点数加入到答案中

    记得开一个标志数组,避免重复统计并查集

有两个小细节要注意:

  1. 在set的排序函数中,有如下代码:

    1
    2
    3
    4
    bool operator<(const node2 &x) const {
    if (y_ != x.y_) return y_ < x.y_;
    return id_ < x.id_;
    }

    id对于排序本身没啥用;

    但是由于set的特殊机制,若不将id纳入排序规则中,只要y相同就会被去重删除(但实际上节点都不同)

  2. 若两点已经在同一并查集内,就不要重复合并了

    1
    2
    int a = find(x), b = find(y);
    if (a == b) return;